{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "44c1ed83-f792-4f00-bbd6-8e0fb29448a6",
   "metadata": {},
   "source": [
    "# <center> LangGraph快速入门与Agent开发实战\n",
    "# <center> Part 1.LangGraph快速入门"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f70a667-ac68-46c4-b40a-53b9bb5a3b35",
   "metadata": {},
   "source": [
    "## 一、LangGraph 生态介绍"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "871f1212-5c3b-47f1-8c95-bd47d3ca68d5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;随着大模型应用日趋复杂，开发者需要一种既能利用 `Agent` 灵活性又能保证可靠性的基础设施。`LangChain`通过 “Chain” 工作流（固定顺序步骤）虽然简单但缺乏适应性，而完全自主的 `Agent` 虽灵活却容易不可控。`LangGraph` 正是在这种背景下出现，核心定位是低门槛构建“可控且持久”的智能代理流程，以平衡确定性和自主性的取舍\n",
    "\n",
    "&emsp;&emsp;`LangGraph` 是由 `LangChain AI` 团队于 2023 年 8 月 11 日正式开源的 `AI Agent` 框架，旨在支撑和编排复杂的生成式 `AI Agent` 工作流。`LangGraph` 主要针对构建长时间运行、可回溯的智能体流程所面临的挑战提供一些特定的解决方案，包括：\n",
    "\n",
    "- **可靠性与持续性：** 提供持久化执行能力，使 `Agent` 即使在失败后也能从中断处恢复，支持长时任务不中断运行，例如在复杂业务流程或多轮对话中，`Agent` 可以保存中间状态，避免上下文丢失。\n",
    "\n",
    "- **状态与记忆：** 内置短期工作记忆和长期存储记忆，允许 `Agent` 跨对话会话保留上下文，这解决了以往仅依赖模型上下文窗口导致的记忆局限，使 `Agent` 能“记住”历史互动，提供更连贯的服务。\n",
    "\n",
    "- **人机协作与监督：** 引入Human-in-the-loop（人类在环）机制，支持在关键步骤由人审核或干预，以防止`Agent`偏离正确轨道，这使企业能在敏感任务中加入人工把关，提升最终结果可信度。\n",
    "\n",
    "- **调试与可观测性：** 通过与 `LangSmith` 、`LangGraph Studio`等工具集成，实现对复杂 `Agent` 行为的可视化追踪和调试，开发者可以观察每步决策、工具调用、状态变化，方便定位问题，优化 `Agent` 策略。\n",
    "\n",
    "- **生产级扩展：** 提供面向生产的部署支持，包括水平扩展、任务队列、缓存重试等容错机制，帮助 `Agent` 应对大规模并发和长流程的稳定运行。这使企业能在敏感任务中加入人工把关，提升最终结果可信度。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f972243b-c699-415d-a39e-e36b7e79e234",
   "metadata": {},
   "source": [
    "&emsp;&emsp;其官方开源地址：https://github.com/langchain-ai/langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8d0d356-6f2a-44af-a33a-cbd3f31bf0e7",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506161908483.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8aee5924-f705-45a8-900e-8ee54f8f2982",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`LangGraph` 本质上是对原有 `LangChain` 功能的演进和补充。`LangChain` 一直以简洁的接口帮助开发者将大模型应用串联（链式）起来，实现提示模板、外部工具调用、Retrieval等功能。如下图所示：\n",
    "\n",
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506161910309.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2a652b8-a129-4c53-b14c-3114e33e8c13",
   "metadata": {},
   "source": [
    "&emsp;&emsp;然而，随着用例复杂度提升，`LangChain` 原有的链式代理在状态管理、多分支流程等方面显得局限。`LangGraph` 因此作为独立库发布（Python 和 JS 版本均有），但与 `LangChain` 无缝集成：开发者仍可利用 `LangChain` 丰富的模型接口、向量库、工具集，只是用 `LangGraph` 来编排更复杂的控制流。可以理解为 “`LangChain` 提供组件，`LangGraph` 负责编排”，两者协同构建完整应用。\n",
    "\n",
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506161912201.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "943bebdc-cead-4ae3-acc5-633c06bbe1b5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;顾名思义，`LangGraph` 框架的核心理念是图，**它的出现是`要解决线性序列的局限性问题，而解决的方法就是循环图`** 在`LangGraph`框架中，**用图取代了`LangChain`的`AgentExecutor`（代理执行器），用来管理代理的生命周期并在其状态内将暂存器作为消息进行跟踪，增加了以循环方式跨各种计算步骤协调多个链或参与者的功能。**这就与 `LangChain` 将代理视为可以附加工具和插入某些提示的对象不同，对于图来说，意味着**我们可以从任何可运行的功能或代理或链作为一个程序的起点。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "970d0451-8599-46ec-8405-e1d5d244832d",
   "metadata": {},
   "source": [
    "&emsp;&emsp;因此，其实`LangChain` 与 `LangGraph` 是互补关系：但相较于`LangChain`，`LangGraph`是一个适用范围更广的`AI Agent`开发框架。在**大模型的支持方面**，`LangGraph`支持`DeepSeek`、`Qwen 3`等，还兼容其他多种在线或开源模型，例如 `OpenAI`、`llama3`等，可以说热门的大模型均可以接入到该框架中进行`AI Agent`应用程序的开发。而关于**大模型的接入方式**，我们既可以通过传统的`openai api`等原生方式将大模型集成到`LangGraph`构建的`AI Agent`流程中，也可以利用`ollma`、`vllm`等大模型推理加速库，实现更加便捷和高效的集成。除此之外，**在`AI Agent`的构建范式上**，`LangGraph`不仅提供了预配置的`ReAct`代理机制，还支持更多自定义的如`Planning`策略的接入，以满足不同应用场景的需求。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c30b5b64-ebdc-4daa-81ab-898f02749db1",
   "metadata": {},
   "source": [
    "&emsp;&emsp;当前业界除了 `LangGraph`，还有多种框架支持 `Agent` 应用开发，如微软的 `AutoGen`、开源平台 `Dify`、谷歌的 `Agent Development Kit (ADK)` 等等，与其他框架相比，`LangGraph` 最大的优势在于流程编排的灵活性、原生支持持久化记忆和强大的人机协同机制。它适合企业级、任务复杂、需要过程审计的业务场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ffb13e3-3bee-46fc-8804-e50e267bec7b",
   "metadata": {},
   "source": [
    "<style>\n",
    ".center \n",
    "{\n",
    "  width: auto;\n",
    "  display: table;\n",
    "  margin-left: auto;\n",
    "  margin-right: auto;\n",
    "}\n",
    "</style>\n",
    "\n",
    "<p align=\"center\"><font face=\"黑体\" size=4>LangGraph 与其它框架对比</font></p>\n",
    "<div class=\"center\">\n",
    "\n",
    "| 框架       | 架构特点             | 主要优势                             | 适用场景                   | 局限/劣势                            |\n",
    "|------------|----------------------|--------------------------------------|----------------------------|---------------------------------------|\n",
    "| LangGraph  | 图流程编排+状态持久  | 流程灵活、可控、原生记忆、人机协同   | 企业复杂流程、定制化Agent  | 考验开发人员的研发能力               |\n",
    "| AutoGen    | 多Agent对话协作      | 自由协作、创新AI社会实验             | 多智能体协作/AI研究        | 流程难控，生产可用性弱                |\n",
    "| Dify       | 平台化+零代码        | 快速开发、可视化、插件丰富           | 快速原型/简单业务集成      | 自由度低，复杂流程定制受限            |\n",
    "| Google ADK | 云原生+企业集成      | 易部署、性能强    | 大型企业/谷歌云用户        | 生态绑定谷歌，灵活度相对较低              |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9aa9d213-85de-4a1e-acf5-7862ec9472e6",
   "metadata": {},
   "source": [
    "- LangChain公开课参考：https://www.bilibili.com/video/BV1pYKgzAE5C/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03d6a31e-e50a-494b-b4ed-8f6e00621d61",
   "metadata": {},
   "source": [
    "<center><img src=\"https://ml2022.oss-cn-hangzhou.aliyuncs.com/img/image-20250624201947404.png\" alt=\"image-20250624201947404\" style=\"zoom: 33%;\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41ed83f6-9ab3-4182-85be-d75c127a5ab1",
   "metadata": {},
   "source": [
    "## 二、LangGraph图结构对象创建方法与核心语法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8c3c5be-e9f6-45f9-a06c-165a2383fca9",
   "metadata": {},
   "source": [
    "&emsp;&emsp;本次公开课使用的版本是：`langgraph==0.4.8`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c4e6b0e1-5ce3-4ba3-b27d-7035b2717c31",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting langgraph\n",
      "  Downloading langgraph-0.4.8-py3-none-any.whl.metadata (6.8 kB)\n",
      "Requirement already satisfied: langchain-core>=0.1 in /root/anaconda3/lib/python3.12/site-packages (from langgraph) (0.3.65)\n",
      "Collecting langgraph-checkpoint>=2.0.26 (from langgraph)\n",
      "  Downloading langgraph_checkpoint-2.1.0-py3-none-any.whl.metadata (4.2 kB)\n",
      "Collecting langgraph-prebuilt>=0.2.0 (from langgraph)\n",
      "  Downloading langgraph_prebuilt-0.2.2-py3-none-any.whl.metadata (4.5 kB)\n",
      "Collecting langgraph-sdk>=0.1.42 (from langgraph)\n",
      "  Downloading langgraph_sdk-0.1.70-py3-none-any.whl.metadata (1.5 kB)\n",
      "Requirement already satisfied: pydantic>=2.7.4 in /root/anaconda3/lib/python3.12/site-packages (from langgraph) (2.11.4)\n",
      "Requirement already satisfied: xxhash>=3.5.0 in /root/anaconda3/lib/python3.12/site-packages (from langgraph) (3.5.0)\n",
      "Requirement already satisfied: langsmith<0.4,>=0.3.45 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (0.3.45)\n",
      "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (8.5.0)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (1.33)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (6.0.1)\n",
      "Requirement already satisfied: packaging<25,>=23.2 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (24.1)\n",
      "Requirement already satisfied: typing-extensions>=4.7 in /root/anaconda3/lib/python3.12/site-packages (from langchain-core>=0.1->langgraph) (4.13.2)\n",
      "Collecting ormsgpack>=1.10.0 (from langgraph-checkpoint>=2.0.26->langgraph)\n",
      "  Downloading ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (43 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m43.7/43.7 kB\u001b[0m \u001b[31m14.2 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: httpx>=0.25.2 in /root/anaconda3/lib/python3.12/site-packages (from langgraph-sdk>=0.1.42->langgraph) (0.28.1)\n",
      "Requirement already satisfied: orjson>=3.10.1 in /root/anaconda3/lib/python3.12/site-packages (from langgraph-sdk>=0.1.42->langgraph) (3.10.13)\n",
      "Requirement already satisfied: annotated-types>=0.6.0 in /root/anaconda3/lib/python3.12/site-packages (from pydantic>=2.7.4->langgraph) (0.6.0)\n",
      "Requirement already satisfied: pydantic-core==2.33.2 in /root/anaconda3/lib/python3.12/site-packages (from pydantic>=2.7.4->langgraph) (2.33.2)\n",
      "Requirement already satisfied: typing-inspection>=0.4.0 in /root/anaconda3/lib/python3.12/site-packages (from pydantic>=2.7.4->langgraph) (0.4.0)\n",
      "Requirement already satisfied: anyio in /root/anaconda3/lib/python3.12/site-packages (from httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (4.8.0)\n",
      "Requirement already satisfied: certifi in /root/anaconda3/lib/python3.12/site-packages (from httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (2024.2.2)\n",
      "Requirement already satisfied: httpcore==1.* in /root/anaconda3/lib/python3.12/site-packages (from httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (1.0.7)\n",
      "Requirement already satisfied: idna in /root/anaconda3/lib/python3.12/site-packages (from httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (3.7)\n",
      "Requirement already satisfied: h11<0.15,>=0.13 in /root/anaconda3/lib/python3.12/site-packages (from httpcore==1.*->httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (0.14.0)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in /root/anaconda3/lib/python3.12/site-packages (from jsonpatch<2.0,>=1.33->langchain-core>=0.1->langgraph) (2.1)\n",
      "Requirement already satisfied: requests<3,>=2 in /root/anaconda3/lib/python3.12/site-packages (from langsmith<0.4,>=0.3.45->langchain-core>=0.1->langgraph) (2.32.3)\n",
      "Requirement already satisfied: requests-toolbelt<2.0.0,>=1.0.0 in /root/anaconda3/lib/python3.12/site-packages (from langsmith<0.4,>=0.3.45->langchain-core>=0.1->langgraph) (1.0.0)\n",
      "Requirement already satisfied: zstandard<0.24.0,>=0.23.0 in /root/anaconda3/lib/python3.12/site-packages (from langsmith<0.4,>=0.3.45->langchain-core>=0.1->langgraph) (0.23.0)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /root/anaconda3/lib/python3.12/site-packages (from requests<3,>=2->langsmith<0.4,>=0.3.45->langchain-core>=0.1->langgraph) (2.0.4)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /root/anaconda3/lib/python3.12/site-packages (from requests<3,>=2->langsmith<0.4,>=0.3.45->langchain-core>=0.1->langgraph) (2.2.2)\n",
      "Requirement already satisfied: sniffio>=1.1 in /root/anaconda3/lib/python3.12/site-packages (from anyio->httpx>=0.25.2->langgraph-sdk>=0.1.42->langgraph) (1.3.0)\n",
      "Downloading langgraph-0.4.8-py3-none-any.whl (152 kB)\n",
      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m152.4/152.4 kB\u001b[0m \u001b[31m6.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:02\u001b[0m\n",
      "\u001b[?25hDownloading langgraph_checkpoint-2.1.0-py3-none-any.whl (43 kB)\n",
      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m43.8/43.8 kB\u001b[0m \u001b[31m26.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
      "\u001b[?25hDownloading langgraph_prebuilt-0.2.2-py3-none-any.whl (23 kB)\n",
      "Downloading langgraph_sdk-0.1.70-py3-none-any.whl (49 kB)\n",
      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m50.0/50.0 kB\u001b[0m \u001b[31m27.4 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
      "\u001b[?25hDownloading ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (216 kB)\n",
      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m216.7/216.7 kB\u001b[0m \u001b[31m10.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:02\u001b[0m\n",
      "\u001b[?25hInstalling collected packages: ormsgpack, langgraph-sdk, langgraph-checkpoint, langgraph-prebuilt, langgraph\n",
      "Successfully installed langgraph-0.4.8 langgraph-checkpoint-2.1.0 langgraph-prebuilt-0.2.2 langgraph-sdk-0.1.70 ormsgpack-1.10.0\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "! pip install langgraph -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b9174fa3-79e8-4669-89ac-7f70d1b40784",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: langgraph\n",
      "Version: 0.4.8\n",
      "Summary: Building stateful, multi-actor applications with LLMs\n",
      "Home-page: \n",
      "Author: \n",
      "Author-email: \n",
      "License: \n",
      "Location: /root/anaconda3/lib/python3.12/site-packages\n",
      "Requires: langchain-core, langgraph-checkpoint, langgraph-prebuilt, langgraph-sdk, pydantic, xxhash\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "! pip show langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02b6b9fa-4978-471c-a625-21d3e2bc0ffb",
   "metadata": {},
   "source": [
    "### 1. LangChain图结构概念说明"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0611ac51-b4b6-439b-92a8-0da17cd40536",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在以图构建的框架中，**任何可执行的功能都可以作为对话、代理或程序的启动点**。这个启动点可以是大模型的 `API` 接口、基于大模型构建的 `AI Agent`，通过 `LangChain` 或其他技术建立的线性序列等等，即下图中的 \"Start\" 圆圈所示。无论哪种形式，它都首先处理用户的输入，并决定接下来要做什么。下图展示了在 `LangGraph` 概念下，最基本的一种代理模型：👇\n",
    "\n",
    "<div align=center><img src=\"https://muyu001.oss-cn-beijing.aliyuncs.com/img/1011002.png\" width=50%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b4ae13a-99e8-47ff-a0cc-f338c9acd236",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如上图所示，**在启动点定义的可运行功能会根据收到的输入决定是否进行检索以及如何响应。** 比如在执行过程中，如果需要检索信息，则可以利用搜索工具来实现，如`Web Search`（网络搜索）、`Query Database`（查询数据库）、`RAG`等获取必要的信息（图中的 \"Action\" 圆圈）。接下来，再使用一个大语言模型（“LLM”）处理工具提供的信息，结合用户最初传入的初始查询，生成最终的响应（图中的 \"LLMs\" 圆圈）。最终，这个响应被传递至终点节点（图中的 \"End\" 圆圈）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c98ded39-b7ab-41dd-8030-23fcfbd197e4",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这个流程就是在`LangGraph`框架中一个非常简单的代理构成形式。非常关键且我们必须清楚的概念是：**每个圆圈代表一个“节点”（Nodes），每个箭头表示一条“边”（Edges）。在 `LangGraph` 中，无论代理的构建是简单还是复杂，它最终都是由节点和边通过特定的组合形成的图。这样的构建形式形成的工作流原理就是：当每个节点完成工作后，通过边告诉下一步该做什么，所以也就得出了：`LangGraph`的底层图算法就是在使用消息传递来定义通用程序。当节点完成其操作时，它会沿着一条或多条边向其他节点发送消息。然后，这些接收节点执行其功能，将结果消息传递给下一组节点，然后该过程继续。如此循环往复。**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "801ecaae-c2b4-4764-8ab1-aab70c1a6ea3",
   "metadata": {},
   "source": [
    "&emsp;&emsp;**这就是`LangGraph`底层架构设计中图算法的根本思想。**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4af934c2-d659-4af7-ab78-0ce79aa83b38",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`LangGraph`框架是通过组合`Nodes`和`Edges`去创建复杂的循环工作流程，通过消息传递的方式串联所有的节点形成一个通路。**那么维持消息能够及时的更新并向该去的地方传递，则依赖`langGraph`构建的`State`概念。** 在`LangGraph`构建的流程中，每次执行都会启动一个状态，图中的节点在处理时会传递和修改该状态。这个状态不仅仅是一组静态数据，而是由每个节点的输出动态更新，然后影响循环内的后续操作。如下所示：👇"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "579b3429-f8f3-4ec0-b26c-b5d966ca80e4",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu001.oss-cn-beijing.aliyuncs.com/img/101104.png\" width=90%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6fca3cf-2950-49e4-a1ee-30adc1843171",
   "metadata": {},
   "source": [
    "&emsp;&emsp;为了帮助大家更好的理解，我们先尝试在不接入大模型的情况下，构建一个如上图所示的简单工作流。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de565f11-fdd7-44f5-98c2-89c4723ee77c",
   "metadata": {},
   "source": [
    "### 2.手动构建图流程"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "802178aa-e680-4407-84fe-b026ef256eea",
   "metadata": {},
   "source": [
    "&emsp;&emsp;定义图时要做的第一件事是定义图的`State`。状态表示会随着图计算的进行而维护和更新的上下文或记忆。它用来确保图中的每个步骤都可以访问先前步骤的相关信息，从而可以根据整个过程中积累的数据进行动态决策。这个过程通过状态图`StateGraph`类实现，它是由`LangGraph`框架提供的核心类之一，专门用来创建`state`状态。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2599770-4559-46b4-bfb9-5df3e91033b2",
   "metadata": {},
   "source": [
    "&emsp;&emsp;构建`state`的方法非常简答。我们可以将图的状态设计为一个字典，用于在不同节点间共享和修改数据，然后使用`StateGraph`类进行图的实例化。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6a1d4a5f-273b-4c2d-a6df-3c2713e85f6f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import StateGraph\n",
    "\n",
    "# 使用 stategraph 接收一个字典\n",
    "builder = StateGraph(dict) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "230ca90b-834a-401a-8ec9-5772a3d53ded",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x7fd454a46990>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "builder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "3584376d-1080-4974-bdbb-24bef1011f54",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "builder.schema"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa524862-cff6-404b-9df8-611be8cb8a45",
   "metadata": {},
   "source": [
    "这里需要注意的是，builder也是后面要用到的图构建器（Graph Builder）对象，用于逐步添加节点、边、控制流逻辑，最终编译成可执行的 LangGraph 图。而这个图构建器需要通过带入一个状态对象来创建。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79ae9473-f569-46b4-8c00-b90498838d5a",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来，定义两个节点。`addition`节点是一个加法逻辑，接收当前状态`StateGraph(dict)`，将字典中`x`的值增加1，并返回新的状态。而`subtraction`节点是一个减法逻辑，接收从`addition`节点传来的状态`StateGraph(dict)`，从字典中的`x`值减去2，创建并返回一个新的键y。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9a67d3db-505a-4b9e-b06b-907eec3015d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def addition(state):\n",
    "    # 注意：这里接收到的是初始状态\n",
    "    print(f\"init_state: {state}\")\n",
    "    return {\"x\": state[\"x\"] + 1}\n",
    "\n",
    "def subtraction(state):\n",
    "    # 注意：这里接收到的是上一个节点的状态\n",
    "    print(f\"addition_state: {state}\")\n",
    "    return {\"x\": state[\"x\"] - 2}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba589213-f4f6-4075-8070-8d0d1db1cde8",
   "metadata": {},
   "source": [
    "&emsp;&emsp;然后，进行图结构的设计。具体来看，我们添加名为`addition`和`subtraction`的节点，并关联到上面定义的函数。设定图的起始节点为`addition`，并从`addition`到`subtraction`设置一条边，最后从`subtraction`到结束节点设置另一条边。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "41f04aea-0089-46f1-8b55-96fef7d66f18",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x7fd454a46990>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# START 和 END 是两个特殊的节点，分别表示图的开始和结束。\n",
    "from langgraph.graph import START, END\n",
    "\n",
    "# 向图中添加两个节点\n",
    "builder.add_node(\"addition\", addition)\n",
    "builder.add_node(\"subtraction\", subtraction)\n",
    "\n",
    "# 构建节点之间的边\n",
    "builder.add_edge(START, \"addition\")\n",
    "builder.add_edge(\"addition\", \"subtraction\")\n",
    "builder.add_edge(\"subtraction\", END)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc9a54c2-f57b-4aa9-9cc7-5d3dc2474fe6",
   "metadata": {},
   "source": [
    "> 注，这里我们通过节点的同名字符串作为节点的名称。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9c281f76-581c-452f-9ec9-57dc2d16aeaf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{('__start__', 'addition'),\n",
       " ('addition', 'subtraction'),\n",
       " ('subtraction', '__end__')}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "builder.edges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "87408e68-1524-4909-b73f-6b645b75d41c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'addition': StateNodeSpec(runnable=addition(tags=None, recurse=True, explode_args=False, func_accepts_config=False, func_accepts={}), metadata=None, input=<class 'dict'>, retry_policy=None, cache_policy=None, ends=(), defer=False),\n",
       " 'subtraction': StateNodeSpec(runnable=subtraction(tags=None, recurse=True, explode_args=False, func_accepts_config=False, func_accepts={}), metadata=None, input=<class 'dict'>, retry_policy=None, cache_policy=None, ends=(), defer=False)}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "builder.nodes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06109bef-f904-4356-bf67-a96ea9f5854a",
   "metadata": {},
   "source": [
    "&emsp;&emsp;最后，执行图的编译。需要通过调用`compile()`方法将这些设置编译成一个可执行的图。代码如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5230cb44-45f9-4b53-af5c-53fa6fef75fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = builder.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86c78759-9c2d-4fa8-9d02-94c20251aef6",
   "metadata": {},
   "source": [
    "&emsp;&emsp;除了上述通过打印的方式查看构建图的结构，`LangGraph`还提供了多种内置的图形可视化方法，能够将任何`Graph`以图形的形式展示出来，帮助我们更好地理解节点之间的关系和流程的动态变化。**可视化最大的好处是：直接从代码中生成图形化的表示，可以检查图的执行逻辑是否符合构建的预期。**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d32f35e5-935b-4d41-ae8b-d7aa182b91b8",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们首先需要执行如下代码安装图可视化依赖的第三方库："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c97709a9-f8af-4c4c-944d-0996b2b744f2",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: pyppeteer in /root/anaconda3/lib/python3.12/site-packages (2.0.0)\n",
      "Requirement already satisfied: ipython in /root/anaconda3/lib/python3.12/site-packages (8.25.0)\n",
      "Requirement already satisfied: appdirs<2.0.0,>=1.4.3 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (1.4.4)\n",
      "Requirement already satisfied: certifi>=2023 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (2024.2.2)\n",
      "Requirement already satisfied: importlib-metadata>=1.4 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (7.0.1)\n",
      "Requirement already satisfied: pyee<12.0.0,>=11.0.0 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (11.1.1)\n",
      "Requirement already satisfied: tqdm<5.0.0,>=4.42.1 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (4.66.4)\n",
      "Requirement already satisfied: urllib3<2.0.0,>=1.25.8 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (1.26.20)\n",
      "Requirement already satisfied: websockets<11.0,>=10.0 in /root/anaconda3/lib/python3.12/site-packages (from pyppeteer) (10.4)\n",
      "Requirement already satisfied: decorator in /root/anaconda3/lib/python3.12/site-packages (from ipython) (5.1.1)\n",
      "Requirement already satisfied: jedi>=0.16 in /root/anaconda3/lib/python3.12/site-packages (from ipython) (0.18.1)\n",
      "Requirement already satisfied: matplotlib-inline in /root/anaconda3/lib/python3.12/site-packages (from ipython) (0.1.6)\n",
      "Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /root/anaconda3/lib/python3.12/site-packages (from ipython) (3.0.51)\n",
      "Requirement already satisfied: pygments>=2.4.0 in /root/anaconda3/lib/python3.12/site-packages (from ipython) (2.15.1)\n",
      "Requirement already satisfied: stack-data in /root/anaconda3/lib/python3.12/site-packages (from ipython) (0.2.0)\n",
      "Requirement already satisfied: traitlets>=5.13.0 in /root/anaconda3/lib/python3.12/site-packages (from ipython) (5.14.3)\n",
      "Requirement already satisfied: pexpect>4.3 in /root/anaconda3/lib/python3.12/site-packages (from ipython) (4.8.0)\n",
      "Requirement already satisfied: zipp>=0.5 in /root/anaconda3/lib/python3.12/site-packages (from importlib-metadata>=1.4->pyppeteer) (3.17.0)\n",
      "Requirement already satisfied: parso<0.9.0,>=0.8.0 in /root/anaconda3/lib/python3.12/site-packages (from jedi>=0.16->ipython) (0.8.3)\n",
      "Requirement already satisfied: ptyprocess>=0.5 in /root/anaconda3/lib/python3.12/site-packages (from pexpect>4.3->ipython) (0.7.0)\n",
      "Requirement already satisfied: wcwidth in /root/anaconda3/lib/python3.12/site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython) (0.2.13)\n",
      "Requirement already satisfied: typing-extensions in /root/anaconda3/lib/python3.12/site-packages (from pyee<12.0.0,>=11.0.0->pyppeteer) (4.13.2)\n",
      "Requirement already satisfied: executing in /root/anaconda3/lib/python3.12/site-packages (from stack-data->ipython) (0.8.3)\n",
      "Requirement already satisfied: asttokens in /root/anaconda3/lib/python3.12/site-packages (from stack-data->ipython) (2.0.5)\n",
      "Requirement already satisfied: pure-eval in /root/anaconda3/lib/python3.12/site-packages (from stack-data->ipython) (0.2.2)\n",
      "Requirement already satisfied: six in /root/anaconda3/lib/python3.12/site-packages (from asttokens->stack-data->ipython) (1.16.0)\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "! pip install pyppeteer ipython -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de3ea263-4694-4aed-a0e3-9be025587b58",
   "metadata": {},
   "source": [
    "&emsp;&emsp;生成图结构的可视化非常直接，只需一行代码即可完成。具体代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "c3c2f007-5b75-45e5-bb56-5b2cc8893cb2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "472b5e41-f664-4a18-815f-cefc09e2e95c",
   "metadata": {},
   "source": [
    "&emsp;&emsp;当通过 `builder.compile()` 方法编译图后，编译后的 `graph` 对象提供了 `invoke` 方法，该方法用于启动图的执行。我们可以通过 `invoke` 方法传递一个初始状态（如 `initial_state = {\"x\": 10}`），这个状态将作为图执行的起始输入。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "872180f7-fd4b-407d-88d7-9c689d7bd3c1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "init_state: {'x': 10}\n",
      "addition_state: {'x': 11}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'x': 9}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个初始化的状态\n",
    "initial_state = {\"x\":10}\n",
    "\n",
    "graph.invoke(initial_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f615602-74a3-4ac7-bebd-a5244c609a05",
   "metadata": {},
   "source": [
    "LangGraph 的执行模型并不强制要求图中必须有 END 节点。只要执行路径在某个节点“无后继边”（即到达“终点”），那么该节点就被视为隐式终点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a3d2430-14e0-4b79-be4e-8e4815025a92",
   "metadata": {},
   "source": [
    "> 也就是说，将一个字典作为状态对象带入到图中，即可进行图的实际运行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd99022c-75e8-4b5d-8691-6aba4213eb66",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在图的执行过程中，每个节点的函数会被调用，并且接收到前一个节点返回的状态作为输入。每个函数处理完状态后，会输出一个新的状态，传递给下一个节点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "620f1205-7195-4479-a291-b0225e9aaa0d",
   "metadata": {},
   "source": [
    "&emsp;&emsp;上述代码执行过程中图的运行状态如下图所示：👇"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b3c7a8e-bb46-43f0-b170-64a1bb573c4b",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu001.oss-cn-beijing.aliyuncs.com/img/202410220010.png\" width=100%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39c856a1-f1e3-41e5-a053-92af053147be",
   "metadata": {},
   "source": [
    "这里需要注意的一个关键信息是：**节点函数不需要返回整个状态，而是仅返回它们更新的部分。** 也就是说：在每个节点的函数内部逻辑中，需要使用和更新哪些`State`中的参数中，只需要在`return`的时候指定即可，不必担心未在当前节点处理的State中的其他值会丢失，因为`LangGraph`的内部机制已经自动处理了状态的合并和维护。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "cd595a13-ee10-496a-9169-0bd90583e4f8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "init_state: {'x': 10, 'y': 9}\n",
      "addition_state: {'x': 11}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'x': 9}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个初始化的状态\n",
    "initial_state = {\"x\":10, \"y\": 9}\n",
    "\n",
    "graph.invoke(initial_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b13084c-2287-4a1a-a0b5-8adb49052a4e",
   "metadata": {},
   "source": [
    "&emsp;&emsp;总体来看，该图设置了一个简单的工作流程。其中值首先在第一个节点通过加法函数增加，然后在第二个节点通过减法函数减少。这一流程展示了节点如何通过图中的共享状态进行交互。需要注意的是，**状态在任何给定时间只包含来自一个节点的更新信息。这意味着当节点处理状态时，它只能访问与其特定操作直接相关的数据，从而确保每个节点的逻辑是隔离和集中的。** 使用字典作为状态模式非常简单，**由于缺乏预定义的模式，节点可以在没有严格类型约束的情况下自由地读取和写入状态，这样的灵活性有利于动态数据处理。** 然而，这也要求开发者在整个图的执行过程中保持对键和值的一致性管理。因为如果在任何节点中尝试访问`State`中不存在的键，会直接中断整个图的运行状态。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2978bd0-88c8-4b26-847d-266bfd0710bb",
   "metadata": {},
   "source": [
    "### 3. 借助Pydantic对象创建图"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68f3bc54-f3ef-4720-ba5d-72497d7e5d58",
   "metadata": {},
   "source": [
    "&emsp;&emsp;Pydantic 是一个用于创建“数据模型”的 Python 库，它可以自动校验数据类型，并将字典数据转换为结构化对象。它就像是给字典加了一个“类型安全 + 自动验证”的外壳，是现代 Python 项目中最主流的“数据结构定义工具”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "59148dd6-5d17-4a7a-88ff-c6a103d487af",
   "metadata": {},
   "outputs": [],
   "source": [
    "state = {\"x\": 1, \"y\": \"hello\"}  # 没有类型限制\n",
    "\n",
    "state[\"x\"] = \"abc\"  # 不小心改错也不会报错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "7a88ba64-6cdd-4941-8871-03162fa33ca8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'x': 'abc', 'y': 'hello'}"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "5545b4a8-44c5-4980-8f91-5560fbb0e5fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "default\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel\n",
    "\n",
    "class MyState(BaseModel):\n",
    "    x: int\n",
    "    y: str = \"default\"   # 设置默认值\n",
    "\n",
    "# 自动校验\n",
    "state = MyState(x=1)\n",
    "print(state.x)       # 输出 1\n",
    "print(state.y)       # 输出 default\n",
    "\n",
    "# 错误类型会报错\n",
    "# state = MyState(x=\"abc\")  # ❌ 会抛出 ValidationError"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ece982fb-caa7-4e19-af03-d274abf1fa20",
   "metadata": {},
   "source": [
    "然后我们可以在 StateGraph(MyState) 传入一个 Pydantic 模型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "21bff0e0-533c-4fb3-bd8d-f4d8f4175770",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[addition] 初始状态: x=10\n",
      "[subtraction] 接收到状态: x=11\n",
      "\n",
      "[最终结果] -> {'x': 9}\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# ✅ 1. 定义结构化状态模型\n",
    "class CalcState(BaseModel):\n",
    "    x: int\n",
    "\n",
    "# ✅ 2. 定义节点函数，接收并返回 CalcState\n",
    "def addition(state: CalcState) -> CalcState:\n",
    "    print(f\"[addition] 初始状态: {state}\")\n",
    "    return CalcState(x=state.x + 1)\n",
    "\n",
    "def subtraction(state: CalcState) -> CalcState:\n",
    "    print(f\"[subtraction] 接收到状态: {state}\")\n",
    "    return CalcState(x=state.x - 2)\n",
    "\n",
    "# ✅ 3. 构建图\n",
    "builder = StateGraph(CalcState)\n",
    "\n",
    "builder.add_node(\"addition\", addition)\n",
    "builder.add_node(\"subtraction\", subtraction)\n",
    "\n",
    "builder.add_edge(START, \"addition\")\n",
    "builder.add_edge(\"addition\", \"subtraction\")\n",
    "builder.add_edge(\"subtraction\", END)\n",
    "\n",
    "graph = builder.compile()\n",
    "\n",
    "# ✅ 4. 执行图：传入结构化状态对象\n",
    "initial_state = CalcState(x=10)\n",
    "final_state = graph.invoke(initial_state)\n",
    "\n",
    "# ✅ 5. 打印最终结果\n",
    "print(\"\\n[最终结果] ->\", final_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fa404ed-0f9d-4a62-acf5-a6a0b31f234b",
   "metadata": {},
   "source": [
    "但是需要注意的是，无论输入端输入什么结构的对象，最终图计算返回结果是一个字典类型对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8056afe3-5c1d-4edd-a559-6c8f23b2d480",
   "metadata": {},
   "source": [
    "### 4.创建条件分支图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "d9114744-d2e0-4284-81e8-4203c3360e4a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✅ 测试 x=4（偶数）\n",
      "[check_x] Received state: x=4 result=None\n",
      "[handle_even] x 是偶数\n",
      "\n",
      "✅ 测试 x=3（奇数）\n",
      "[check_x] Received state: x=3 result=None\n",
      "[handle_odd] x 是奇数\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'x': 3, 'result': 'odd'}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import Optional\n",
    "from pydantic import BaseModel\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# ✅ 定义结构化状态\n",
    "class MyState(BaseModel):\n",
    "    x: int\n",
    "    result: Optional[str] = None\n",
    "\n",
    "# ✅ 定义各节点处理逻辑（接受 MyState，返回 MyState）\n",
    "def check_x(state: MyState) -> MyState:\n",
    "    print(f\"[check_x] Received state: {state}\")\n",
    "    return state\n",
    "\n",
    "def is_even(state: MyState) -> bool:\n",
    "    return state.x % 2 == 0\n",
    "\n",
    "def handle_even(state: MyState) -> MyState:\n",
    "    print(\"[handle_even] x 是偶数\")\n",
    "    return MyState(x=state.x, result=\"even\")\n",
    "\n",
    "def handle_odd(state: MyState) -> MyState:\n",
    "    print(\"[handle_odd] x 是奇数\")\n",
    "    return MyState(x=state.x, result=\"odd\")\n",
    "\n",
    "# ✅ 构建图\n",
    "builder = StateGraph(MyState)\n",
    "\n",
    "builder.add_node(\"check_x\", check_x)\n",
    "builder.add_node(\"handle_even\", handle_even)\n",
    "builder.add_node(\"handle_odd\", handle_odd)\n",
    "\n",
    "# ✅ 添加条件分支\n",
    "builder.add_conditional_edges(\"check_x\", is_even, {\n",
    "    True: \"handle_even\",\n",
    "    False: \"handle_odd\"\n",
    "})\n",
    "\n",
    "# ✅ 衔接起始和结束\n",
    "builder.add_edge(START, \"check_x\")\n",
    "builder.add_edge(\"handle_even\", END)\n",
    "builder.add_edge(\"handle_odd\", END)\n",
    "\n",
    "# ✅ 编译图\n",
    "graph = builder.compile()\n",
    "\n",
    "# ✅ 执行测试\n",
    "print(\"\\n✅ 测试 x=4（偶数）\")\n",
    "graph.invoke(MyState(x=4))\n",
    "\n",
    "print(\"\\n✅ 测试 x=3（奇数）\")\n",
    "graph.invoke(MyState(x=3))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cab403e-2821-4e7a-abc4-a2e2cc8eb84a",
   "metadata": {},
   "source": [
    "在本示例中，我们基于 LangGraph 框架构建了一个简单的**有状态条件分支图**，用于演示如何使用结构化状态（通过 Pydantic 模型定义）在多步骤的决策流程中进行状态传递与条件控制。\n",
    "\n",
    "我们首先定义了一个名为 `MyState` 的 Pydantic 模型，用于描述图中每个节点共享的上下文状态信息。该状态包含两个字段：`x` 表示输入数值，`result` 表示最终处理结果。通过使用 Pydantic，能够显著增强状态管理的类型安全性、可读性和扩展性。\n",
    "\n",
    "图的结构由三个核心节点组成：\n",
    "\n",
    "1. **check\\_x**：作为图的第一步处理节点，接收初始状态并进行输出转发，不修改任何字段；\n",
    "2. **handle\\_even**：处理偶数情况，标记 `result = \"even\"`；\n",
    "3. **handle\\_odd**：处理奇数情况，标记 `result = \"odd\"`。\n",
    "\n",
    "在 `check_x` 节点之后，我们引入了基于 `is_even` 判断函数的条件分支控制逻辑。LangGraph 提供的 `add_conditional_edges` 方法允许我们根据状态中的值动态跳转至不同的执行路径，从而实现类似于传统编程语言中的 `if-else` 分支控制。\n",
    "\n",
    "整体流程图如下：\n",
    "\n",
    "```\n",
    "START → check_x → [判断 x 是否为偶数]\n",
    "                     ├─ True  → handle_even → END\n",
    "                     └─ False → handle_odd  → END\n",
    "```\n",
    "\n",
    "该图的编排逻辑清晰地展现了 LangGraph 的优势所在：即在状态流转的基础上，灵活地控制流程分支，并通过结构化模型传递和管理上下文信息，为构建具备复杂控制流的智能体（Agent）打下了基础。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "312d8d80-c958-44d2-a357-d3d916efd8a4",
   "metadata": {},
   "source": [
    "其中\n",
    "```python\n",
    "# ✅ 定义结构化状态\n",
    "class MyState(BaseModel):\n",
    "    x: int\n",
    "    result: Optional[str] = None\n",
    "```\n",
    "代码解释如下"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e2724d9-a7f3-4a97-a557-3b541b5ec8ec",
   "metadata": {},
   "source": [
    "| 字段       | 类型定义                                 | 是否必填 | 默认值        | 含义                       |\n",
    "| -------- | ------------------------------------ | ---- | ---------- | ------------------------ |\n",
    "| `x`      | `int`                                | ✅ 必填 | 无默认值       | 必须提供的整数字段                |\n",
    "| `result` | `Optional[str]` = `Union[str, None]` | ❌ 可选 | 默认是 `None` | 表示一个可选的字符串，常用于延迟赋值或非必要字段 |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ef1a0ff-e3c3-4a82-bcab-253f2ee16205",
   "metadata": {},
   "source": [
    "而完整的图结构执行流程如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2f54ab1-0c62-4f73-9aaf-7167f15fe71f",
   "metadata": {},
   "source": [
    "| 参数位置  | 传入值                                          | 含义                                |\n",
    "| ----- | -------------------------------------------- | --------------------------------- |\n",
    "| 第1个参数 | `\"check_x\"`                                  | 当前执行完的节点名称（分支判断起点）                |\n",
    "| 第2个参数 | `is_even`                                    | 一个接收状态 `state` 并返回布尔值的函数，用于判断分支条件 |\n",
    "| 第3个参数 | `{True: \"handle_even\", False: \"handle_odd\"}` | 条件结果 → 目标节点的映射表                   |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "a668b05b-e0c0-4e3e-97bd-7f5b552e9d5d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import display, Image\n",
    "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4db11e2-6036-4398-aa5c-637fe2d0c9db",
   "metadata": {},
   "source": [
    "### 4.创建条件循环图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "7b357c6c-71f2-42db-bdde-77006e6a1c9b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✅ 执行循环直到 x > 10\n",
      "[increment] 当前 x = 6\n",
      "[increment] 当前 x = 7\n",
      "[increment] 当前 x = 8\n",
      "[increment] 当前 x = 9\n",
      "[increment] 当前 x = 10\n",
      "[最终结果] -> x = 11\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# ✅ 1. 定义结构化状态模型\n",
    "class LoopState(BaseModel):\n",
    "    x: int\n",
    "\n",
    "# ✅ 2. 定义节点逻辑\n",
    "def increment(state: LoopState) -> LoopState:\n",
    "    print(f\"[increment] 当前 x = {state.x}\")\n",
    "    return LoopState(x=state.x + 1)\n",
    "\n",
    "def is_done(state: LoopState) -> bool:\n",
    "    return state.x > 10\n",
    "\n",
    "# ✅ 3. 构建图\n",
    "builder = StateGraph(LoopState)\n",
    "builder.add_node(\"increment\", increment)\n",
    "\n",
    "# ✅ 4. 设置循环控制：is_done 为 True 则结束，否则继续\n",
    "builder.add_conditional_edges(\"increment\", is_done, {\n",
    "    True: END,\n",
    "    False: \"increment\"\n",
    "})\n",
    "\n",
    "builder.add_edge(START, \"increment\")\n",
    "graph = builder.compile()\n",
    "\n",
    "# ✅ 5. 测试执行\n",
    "print(\"\\n✅ 执行循环直到 x > 10\")\n",
    "final_state = graph.invoke(LoopState(x=6))\n",
    "print(f\"[最终结果] -> x = {final_state['x']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7dfc929-1c5f-4cfc-97e3-a6990bd1c73d",
   "metadata": {},
   "source": [
    "注意，这里需要注意，add_conditional_edges中构建了循环"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "22b2fb0c-888f-44d1-a8a2-6105a10d4480",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'x': 11}"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "b1ff714f-2774-44ed-a4b3-10f223044b65",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "0a91b893-159f-4c49-b681-3d17e324fabf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✅ 初始 x=6（偶数，进入循环）\n",
      "[check_x] 当前 x = 6\n",
      "[increment] x 是偶数，执行 +1 → 7\n",
      "[check_x] 当前 x = 7\n",
      "[done] x 是奇数，流程结束\n",
      "[最终结果1] -> {'x': 7, 'done': True}\n",
      "\n",
      "✅ 初始 x=3（奇数，直接 done）\n",
      "[check_x] 当前 x = 3\n",
      "[done] x 是奇数，流程结束\n",
      "[最终结果2] -> {'x': 3, 'done': True}\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel\n",
    "from typing import Optional\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# ✅ 1. 定义状态模型\n",
    "class BranchLoopState(BaseModel):\n",
    "    x: int\n",
    "    done: Optional[bool] = False\n",
    "\n",
    "# ✅ 2. 定义各节点逻辑\n",
    "def check_x(state: BranchLoopState) -> BranchLoopState:\n",
    "    print(f\"[check_x] 当前 x = {state.x}\")\n",
    "    return state\n",
    "\n",
    "def is_even(state: BranchLoopState) -> bool:\n",
    "    return state.x % 2 == 0\n",
    "\n",
    "def increment(state: BranchLoopState) -> BranchLoopState:\n",
    "    print(f\"[increment] x 是偶数，执行 +1 → {state.x + 1}\")\n",
    "    return BranchLoopState(x=state.x + 1)\n",
    "\n",
    "def done(state: BranchLoopState) -> BranchLoopState:\n",
    "    print(f\"[done] x 是奇数，流程结束\")\n",
    "    return BranchLoopState(x=state.x, done=True)\n",
    "\n",
    "# ✅ 3. 构建图\n",
    "builder = StateGraph(BranchLoopState)\n",
    "\n",
    "builder.add_node(\"check_x\", check_x)\n",
    "builder.add_node(\"increment\", increment)\n",
    "builder.add_node(\"done_node\", done)\n",
    "\n",
    "builder.add_conditional_edges(\"check_x\", is_even, {\n",
    "    True: \"increment\",\n",
    "    False: \"done_node\"\n",
    "})\n",
    "\n",
    "# ✅ 4. 循环逻辑：偶数 → increment → check_x\n",
    "builder.add_edge(\"increment\", \"check_x\")\n",
    "\n",
    "# ✅ 5. 起始与终点\n",
    "builder.add_edge(START, \"check_x\")\n",
    "builder.add_edge(\"done_node\", END)\n",
    "\n",
    "graph = builder.compile()\n",
    "\n",
    "# ✅ 6. 测试执行\n",
    "print(\"\\n✅ 初始 x=6（偶数，进入循环）\")\n",
    "final_state1 = graph.invoke(BranchLoopState(x=6))\n",
    "print(\"[最终结果1] ->\", final_state1)\n",
    "\n",
    "print(\"\\n✅ 初始 x=3（奇数，直接 done）\")\n",
    "final_state2 = graph.invoke(BranchLoopState(x=3))\n",
    "print(\"[最终结果2] ->\", final_state2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "bf9d45ca-a728-47c0-a449-51856932b444",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e4ffa5b-0ded-4ffc-a39e-feb1dfcdc9ec",
   "metadata": {},
   "source": [
    "LangGraph 会把所有节点名、状态字段、通道名放在一个命名空间中处理，为了避免歧义，它会严格检查有没有冲突，最保险的做法是：节点名不要与字段名重复，既如果使用 state.result = \"done\"，也不要有 \"result\" 这个节点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90aa05f0-d15e-4e7f-ad44-d1b4d27113a5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;理解了`LangGraph`构建图的基本流程后，接下来我们就尝试接入大模型构建一个对话机器人。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "919571c1-769c-4c10-a989-8a48c86071aa",
   "metadata": {},
   "source": [
    "## 三、搭建基于LangGraph的多轮对话问答机器人"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61e505a6-e7eb-4612-914f-237f43a498d9",
   "metadata": {},
   "source": [
    "### 1. LangGraph中多轮对话实现方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8b57bf3-7c56-46bb-81c2-1c8e0a03af69",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在接下来的这个案例中，我们进一步将大模型接入到 `LangGraph` 工作流程中，并允许动态消息处理以及与模型的交互。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fc6ba48-4a27-4783-a3ae-f4e0f15a5a30",
   "metadata": {},
   "source": [
    "&emsp;&emsp;大模型应用都是接受消息列表作为输入，就像`LangChain`中的`Chat Model`，需要接收`Message`对象列表作为输入。这些消息有多种形式，例如`HumanMessage` （用户输入）或`AIMessage` （ 大模型响应）。这种消息格式其实就与我们之前介绍的`StateGraph(dict)`结构有一些区别。\n",
    "\n",
    "&emsp;&emsp;因此对于消息序列格式的`state`，一种更简单的方法就是使用`LangGraph`预构建的`add_messages`函数，这个更高级的状态所实现的是：**对于全新的消息，它会附加到现有列表，同时它也会正确处理现有消息的更新。** 代码如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "84279426-5377-4d11-935e-b20dc237c464",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a4068b7-e790-457a-ac02-c8699a32d293",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`add_messages`的核心逻辑是合并两个消息列表，按 `ID` 更新现有消息。默认情况下，状态为“仅附加”，当新消息与现有消息具有相同的 ID时，进行更新。具体参数是：\n",
    "- left （ Messages ） – 消息的基本列表。\n",
    "- right （ Messages ） – 要合并到基本列表中的消息列表（或单个消息）。\n",
    "\n",
    "&emsp;&emsp;其返回值是一个消息列表，其中的合并逻辑则是：如果right的消息与left的消息具有相同的 ID，则right的消息将替换left的消息，否则作为一条新的消息进行追加。通过这种形式维护一个`messages`列表，可以很方便的实现消息的合并和更新。其基本形式如下：\n",
    "\n",
    "```python\n",
    "    messages = [\n",
    "        HumanMessage(content=\"你好，请你详细的介绍一下你自己。\"),\n",
    "        AIMessage(content=\"我是一个智能助手，我可以帮助你回答问题。\"),\n",
    "        HumanMessage(content=\"请问什么是大模型？\"),\n",
    "        AIMessage(content=\"大模型是一种基于深度学习的自然语言处理模型，它能够理解和生成自然语言文本。\"),\n",
    "        .....\n",
    "    ]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80be6c3c-3806-4e08-b38a-6da4e1ab1e30",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们可以使用`add_messages`函数来进行快速验证。 如果消息的`ID`不一样，则会进行追加。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "aa53350c-a2a6-465a-9d22-e7d28f56a3e1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='你好。', additional_kwargs={}, response_metadata={}, id='1'),\n",
       " AIMessage(content='你好，很高兴认识你。', additional_kwargs={}, response_metadata={}, id='2')]"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langgraph.graph.message import add_messages\n",
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "\n",
    "msgs1 = [HumanMessage(content=\"你好。\", id=\"1\")]\n",
    "msgs2 = [AIMessage(content=\"你好，很高兴认识你。\", id=\"2\")]\n",
    "\n",
    "add_messages(msgs1, msgs2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d622eb43-0e25-4383-bea6-26b19cf5119b",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果`ID`相同，则会对消息内容进行更新。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "b742cbbe-7442-49ce-8309-9953a93d84e8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='你好呀。', additional_kwargs={}, response_metadata={}, id='1')]"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "msgs1 = [HumanMessage(content=\"你好。\", id=\"1\")]\n",
    "msgs2 = [HumanMessage(content=\"你好呀。\", id=\"1\")]\n",
    "\n",
    "add_messages(msgs1, msgs2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51337a30-d62c-4fdf-b156-93dc8cddfe8c",
   "metadata": {},
   "source": [
    "需要注意的是，不能直接在普通 Python 代码中测试 add_messages 的合并功能。\n",
    "\n",
    "原因是：\n",
    "✅ add_messages 并不会在你创建 State 字典时自动生效，\n",
    "⛔ 它只会在 LangGraph 的内部状态更新系统中被识别和调用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c40e5025-6d3a-4b8f-bd0e-ddd67f44a35e",
   "metadata": {},
   "source": [
    "&emsp;&emsp;因此，当通过`messages: Annotated[list, add_messages]`去定义状态时，我们就可以很方便的实现聊天机器人场景下消息序列的处理和维护。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "574b34e0-fc21-4f27-b16a-6887cb2ef4f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6fd778d-16bb-439c-97f7-f88e6d3ad84d",
   "metadata": {},
   "source": [
    "| 元素             | 含义        | 是什么？                                             |\n",
    "| -------------- | --------- | ------------------------------------------------ |\n",
    "| `list`         | 字段的数据类型   | ✅ Python 内置的**类型**（列表类型）                         |\n",
    "| `add_messages` | 字段的“附加语义” | ✅ LangGraph 提供的**特殊函数（合并器/reducer）**，不是 list 的方法 |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd27cdb2-8453-481e-aa14-7000ab48b3d6",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来我们需要创建一个大模型节点，接收用户的输入，并返回大模型的响应。因此首先需要准备一个可以进行调用的大模型，这里我们选择使用`DeepSeek`的大模型，并使用`DeepSeek`官方的`API_KEK`进行调用。如果初次使用，需要现在`DeepSeek`官网上进行注册并创建一个新的`API_Key`，其官方地址为：https://platform.deepseek.com/usage"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0bc9309-1947-4098-a66d-7964fabcd48c",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506091257041.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ec598f9-a198-4fa9-8158-e0f89532c429",
   "metadata": {},
   "source": [
    "&emsp;&emsp;注册好`DeepSeek`的`API_KEY`后，首先在项目同级目录下创建一个`env`文件，用于存储`DeepSeek`的`API_KEY`，如下所示："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e50f9cf2-03db-476b-846e-6e6e11035142",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506091301526.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2b8ff90-a34a-4606-b0fb-27440f3ec8e0",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来通过`python-dotenv`库读取`env`文件中的`API_KEY`，使其加载到当前的运行环境中，代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "125fa2a7-5793-4e98-8fa3-84b55eb1e6f3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: python-dotenv in /root/anaconda3/lib/python3.12/site-packages (1.0.1)\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "! pip install python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "cb5a3b0f-f630-44aa-8941-ca4a1ee5b8ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv \n",
    "load_dotenv(override=True)\n",
    "\n",
    "DeepSeek_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\n",
    "\n",
    "# print(DeepSeek_API_KEY)  # 可以通过打印查看"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a50d6d6-e7a4-467c-97e8-14ff6614bc13",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们在当前的运行环境直接使用`DeepSeek`的`API`进行网络连通性测试，测试代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f01e000f-f4a5-4830-a660-07b96bdb1564",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: openai in /root/anaconda3/lib/python3.12/site-packages (1.78.1)\n",
      "Requirement already satisfied: anyio<5,>=3.5.0 in /root/anaconda3/lib/python3.12/site-packages (from openai) (4.8.0)\n",
      "Requirement already satisfied: distro<2,>=1.7.0 in /root/anaconda3/lib/python3.12/site-packages (from openai) (1.9.0)\n",
      "Requirement already satisfied: httpx<1,>=0.23.0 in /root/anaconda3/lib/python3.12/site-packages (from openai) (0.28.1)\n",
      "Requirement already satisfied: jiter<1,>=0.4.0 in /root/anaconda3/lib/python3.12/site-packages (from openai) (0.8.2)\n",
      "Requirement already satisfied: pydantic<3,>=1.9.0 in /root/anaconda3/lib/python3.12/site-packages (from openai) (2.11.4)\n",
      "Requirement already satisfied: sniffio in /root/anaconda3/lib/python3.12/site-packages (from openai) (1.3.0)\n",
      "Requirement already satisfied: tqdm>4 in /root/anaconda3/lib/python3.12/site-packages (from openai) (4.66.4)\n",
      "Requirement already satisfied: typing-extensions<5,>=4.11 in /root/anaconda3/lib/python3.12/site-packages (from openai) (4.13.2)\n",
      "Requirement already satisfied: idna>=2.8 in /root/anaconda3/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.7)\n",
      "Requirement already satisfied: certifi in /root/anaconda3/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2024.2.2)\n",
      "Requirement already satisfied: httpcore==1.* in /root/anaconda3/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.7)\n",
      "Requirement already satisfied: h11<0.15,>=0.13 in /root/anaconda3/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n",
      "Requirement already satisfied: annotated-types>=0.6.0 in /root/anaconda3/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.6.0)\n",
      "Requirement already satisfied: pydantic-core==2.33.2 in /root/anaconda3/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.33.2)\n",
      "Requirement already satisfied: typing-inspection>=0.4.0 in /root/anaconda3/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.4.0)\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "! pip install openai -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "ad464b35-c315-42b6-a067-0c80a6075006",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好！我是一个由人工智能驱动的虚拟助手，很高兴认识你。我的主要功能是为你提供信息查询、问题解答、创意建议以及日常事务协助。无论是学习、工作还是生活中的问题，我都会尽力提供有用的帮助。\n",
      "\n",
      "我的特点包括：\n",
      "1. 24小时在线，随时响应\n",
      "2. 能理解多种语言\n",
      "3. 知识覆盖面广(但知识截止到2023年)\n",
      "4. 可以帮你整理思路、提供建议\n",
      "\n",
      "不过我也有些限制：\n",
      "- 没有真实情感和意识\n",
      "- 无法处理需要实地操作的任务\n",
      "- 有时可能会出错\n",
      "\n",
      "你想了解我哪方面的具体信息呢？或者有什么我可以帮你的？\n"
     ]
    }
   ],
   "source": [
    "from openai import OpenAI\n",
    "\n",
    "# 初始化DeepSeek的API客户端\n",
    "client = OpenAI(api_key=DeepSeek_API_KEY, base_url=\"https://api.deepseek.com\")\n",
    "\n",
    "# 调用DeepSeek的API，生成回答\n",
    "response = client.chat.completions.create(\n",
    "    model=\"deepseek-chat\",\n",
    "    messages=[\n",
    "        {\"role\": \"system\", \"content\": \"你是乐于助人的助手，请根据用户的问题给出回答\"},\n",
    "        {\"role\": \"user\", \"content\": \"你好，请你介绍一下你自己。\"},\n",
    "    ],\n",
    ")\n",
    "\n",
    "# 打印模型最终的响应结果\n",
    "print(response.choices[0].message.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d6402c6-68fc-4efc-a485-29ed625e9023",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果可以正常收到`DeepSeek`模型的响应，则说明`DeepSeek`的`API`已经可以正常使用且网络连通性正常。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6183203-bbcd-4e13-b3d4-7d5a33177e2c",
   "metadata": {},
   "source": [
    "&emsp;&emsp;对于`LangGraph`框架，接入大模型最简单的方法就是借助`langChain`中的`ChatModel`组件。因此，我们首先需要安装`LangChain`的`DeepSeek`组件，安装命令如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "733f7a25-53c0-4091-a4cf-5ea8ea0bcb94",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain-deepseek in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (0.1.3)\n",
      "Requirement already satisfied: langchain-core<1.0.0,>=0.3.47 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-deepseek) (0.3.64)\n",
      "Requirement already satisfied: langchain-openai<1.0.0,>=0.3.9 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-deepseek) (0.3.21)\n",
      "Requirement already satisfied: langsmith<0.4,>=0.3.45 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.3.45)\n",
      "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (9.1.2)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (1.33)\n",
      "Requirement already satisfied: PyYAML>=5.3 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (6.0.2)\n",
      "Requirement already satisfied: packaging<25,>=23.2 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (24.2)\n",
      "Requirement already satisfied: typing-extensions>=4.7 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (4.14.0)\n",
      "Requirement already satisfied: pydantic>=2.7.4 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (2.11.5)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from jsonpatch<2.0,>=1.33->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (3.0.0)\n",
      "Requirement already satisfied: openai<2.0.0,>=1.68.2 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (1.84.0)\n",
      "Requirement already satisfied: tiktoken<1,>=0.7 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (0.9.0)\n",
      "Requirement already satisfied: httpx<1,>=0.23.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.28.1)\n",
      "Requirement already satisfied: orjson<4.0.0,>=3.9.14 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (3.10.18)\n",
      "Requirement already satisfied: requests<3,>=2 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (2.32.3)\n",
      "Requirement already satisfied: requests-toolbelt<2.0.0,>=1.0.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (1.0.0)\n",
      "Requirement already satisfied: zstandard<0.24.0,>=0.23.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.23.0)\n",
      "Requirement already satisfied: anyio in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from httpx<1,>=0.23.0->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (4.9.0)\n",
      "Requirement already satisfied: certifi in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from httpx<1,>=0.23.0->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (2025.4.26)\n",
      "Requirement already satisfied: httpcore==1.* in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from httpx<1,>=0.23.0->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (1.0.9)\n",
      "Requirement already satisfied: idna in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from httpx<1,>=0.23.0->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (3.10)\n",
      "Requirement already satisfied: h11>=0.16 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.16.0)\n",
      "Requirement already satisfied: distro<2,>=1.7.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from openai<2.0.0,>=1.68.2->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (1.9.0)\n",
      "Requirement already satisfied: jiter<1,>=0.4.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from openai<2.0.0,>=1.68.2->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (0.10.0)\n",
      "Requirement already satisfied: sniffio in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from openai<2.0.0,>=1.68.2->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (1.3.1)\n",
      "Requirement already satisfied: tqdm>4 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from openai<2.0.0,>=1.68.2->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (4.67.1)\n",
      "Requirement already satisfied: annotated-types>=0.6.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from pydantic>=2.7.4->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.7.0)\n",
      "Requirement already satisfied: pydantic-core==2.33.2 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from pydantic>=2.7.4->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (2.33.2)\n",
      "Requirement already satisfied: typing-inspection>=0.4.0 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from pydantic>=2.7.4->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (0.4.1)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from requests<3,>=2->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (3.4.2)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from requests<3,>=2->langsmith<0.4,>=0.3.45->langchain-core<1.0.0,>=0.3.47->langchain-deepseek) (2.4.0)\n",
      "Requirement already satisfied: regex>=2022.1.18 in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from tiktoken<1,>=0.7->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (2024.11.6)\n",
      "Requirement already satisfied: colorama in e:\\01_木羽研发\\11_trafficvideo\\langchain_venv\\lib\\site-packages (from tqdm>4->openai<2.0.0,>=1.68.2->langchain-openai<1.0.0,>=0.3.9->langchain-deepseek) (0.4.6)\n"
     ]
    }
   ],
   "source": [
    "! pip install langchain-deepseek"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b1eaba3-eeb6-417f-9b52-d1c317390553",
   "metadata": {},
   "source": [
    "&emsp;&emsp;安装好`LangChain`集成`DeepSeek`模型的依赖包后，需要通过一个`init_chat_model`函数来初始化大模型，代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "e8ba6976-9d76-4b77-8fea-6c098703caf6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "913e3e81-038d-4086-bd9e-2abfc24e4ae7",
   "metadata": {},
   "source": [
    "&emsp;&emsp;其中`model`用来指定要使用的模型名称，而`model_provider`用来指定模型提供者，当写入`deepseek`时，会自动加载`langchain-deepseek`的依赖包，并使用在`model`中指定的模型名称用来进行交互。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "06dae636-e6db-4433-8459-09476ad6be80",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好！我是 **DeepSeek Chat**，由深度求索（DeepSeek）公司研发的一款智能AI助手。我的最新版本是 **DeepSeek-V3**，知识截止到 **2024年7月**，具备强大的自然语言理解和生成能力，可以帮助你解答各种问题、提供创意灵感、辅助学习、分析数据等。  \n",
      "\n",
      "### **我的特点：**  \n",
      "✅ **免费使用**：目前没有任何收费计划，你可以随时向我提问！  \n",
      "✅ **超长上下文支持**：我能处理 **128K** 长度的文本，适合阅读和分析长文档。  \n",
      "✅ **文件阅读**：支持上传 **PDF、Word、Excel、PPT、TXT** 等文件，并从中提取信息。  \n",
      "✅ **多语言能力**：可以用中文、英文等多种语言交流，并帮助你翻译和润色文本。  \n",
      "✅ **逻辑与创造力兼具**：无论是数学计算、编程代码，还是写作、头脑风暴，我都能帮上忙！  \n",
      "\n",
      "### **我能帮你做什么？**  \n",
      "📖 **学习辅导**：解答数学、物理、历史等各种学科问题。  \n",
      "💼 **工作助理**：撰写邮件、优化简历、生成报告、分析数据。  \n",
      "📊 **编程助手**：提供代码示例、调试建议、算法优化。  \n",
      "✍ **写作创意**：写小说、诗歌、广告文案、社交媒体帖子。  \n",
      "🌍 **生活百科**：旅行攻略、健康建议、美食推荐等。  \n",
      "\n",
      "如果你有任何问题，尽管问我吧！😊\n"
     ]
    }
   ],
   "source": [
    "question = \"你好，请你介绍一下你自己。\"\n",
    "\n",
    "result = model.invoke(question)\n",
    "print(result.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "427b14a6-7eed-400e-8f05-e10994890018",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这里可以看到，仅仅通过两行代码，我们便可以在`LangChain`中顺利调用`DeepSeek`模型，并得到模型的响应结果。相较于使用`DeepSeek`的`API`，使用`LangChain`调用模型无疑是更加简单的。同时，不仅仅是`DeepSeek`模型，`LangChain`还支持其他很多大模型，如`OpenAI`、`Qwen`、`Gemini`等，我们只需要在`init_chat_model`函数中指定不同的模型名称，就可以调用不同的模型。其工作的原理是这样的："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "609badf6-4000-42cb-8b84-a45ad8650683",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506091353369.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98350199-0f0d-4b4b-865e-0524bb3dd1d1",
   "metadata": {},
   "source": [
    "&emsp;&emsp;理解了这个基本原理，如果大家想在用`LangChain`进行开发时使用其他大模型如`Qwen3`系列，则只需要先获取到`Qwen3`模型的`API_KEY`，然后安装`Tongyi Qwen`的第三方依赖包，即可同样通过`init_chat_model`函数来初始化模型，并调用`invoke`方法来得到模型的响应结果。关于`LangChain`都支持哪些大模型以及每个模型对应的是哪个第三方依赖包，大家可以在`LangChain`的官方文档中找到，访问链接为：https://python.langchain.com/docs/integrations/chat/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2096487a-f506-4d79-a7c1-b56bf256e8c0",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu20241105.oss-cn-beijing.aliyuncs.com/images/202506091359607.png\" width=60%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44497246-47c9-4530-ad92-7858dee89133",
   "metadata": {},
   "source": [
    "&emsp;&emsp;当然，除了在线大模型的接入，`langChain`也只是使用`Ollama`、`vLLM`等框架启动的本地大模型，关于如何使用不同的框架启动如`DeepSeek R1`、`Qwen3`等模型，大家可以参考我的往期公开课视频："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e552f8d8-258f-4e15-8ab3-89a043ee2b20",
   "metadata": {},
   "source": [
    "- LangChain公开课参考：https://www.bilibili.com/video/BV1pYKgzAE5C/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7647fe1-d945-4d04-b1ed-8880b8d66b0d",
   "metadata": {},
   "source": [
    "<center><img src=\"https://ml2022.oss-cn-hangzhou.aliyuncs.com/img/image-20250624201947404.png\" alt=\"image-20250624201947404\" style=\"zoom: 33%;\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9fef22b-c56a-41ac-a68a-09eb2390b8a5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;掌握了如何使用`langChain`接入大模型后，接下来我们可以直接把`langChain`的`Chat Model`接入`LangGraph`中作为一个图节点（`Node`）, 并使用`LangGraph`的`StateGraph`来管理消息序列。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "3682fac7-51c6-4df9-a315-7546a941880d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chatbot(state: State):\n",
    "    return {\"messages\": [model.invoke(state[\"messages\"])]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac00a319-82c2-4a68-b501-f5baac19ffe2",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来，添加一个`chatbot`节点，将当前`State`作为输入并返回一个字典，该字典中更新了`messages`中的状态信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "dec70fc0-49fe-485b-b4a2-b0458ea68509",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x7fd376a6e9c0>"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 添加节点\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "# 添加边\n",
    "graph_builder.add_edge(START, \"chatbot\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "f378d26e-e9d5-4493-a251-4fda333d84b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "graph = graph_builder.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28a58a8b-dee0-4ddd-8dfa-a562cd4c923c",
   "metadata": {},
   "source": [
    "&emsp;&emsp;使用可视化工具，可以很方便的查看图的结构和节点之间的连接关系。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "0b449693-ac03-44d2-8f71-28b3b5c57a24",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d08976d6-93cf-4efd-8988-c7dbdac0b484",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这个图的基本逻辑是：第一个节点调用大模型并生成一个输出，该输出是一个`AIMessage`对象类型，然后，第二个节点直接将前一个节点的 `AIMessage` 提取为具体的`JSON`格式，完成`JSON`的解析。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "922dbe37-9bcf-4414-994a-8e7f234b6a26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [HumanMessage(content='你好，我叫陈明，好久不见。', additional_kwargs={}, response_metadata={}, id='efecc8a9-dbe0-40ad-aedd-f3b741061ae2'), AIMessage(content='你好，陈明！确实好久不见，很高兴能和你再次联系。最近过得怎么样？一切还顺利吗？如果有任何想聊的或者需要帮助的地方，随时告诉我哦～ 😊', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 40, 'prompt_tokens': 12, 'total_tokens': 52, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '39c79867-f3ab-4476-843d-3c0b1f6f8ecd', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--5225dc1c-ab62-415c-a1b0-47d339fa49a0-0', usage_metadata={'input_tokens': 12, 'output_tokens': 40, 'total_tokens': 52, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}\n"
     ]
    }
   ],
   "source": [
    "final_state = graph.invoke({\"messages\": [\"你好，我叫陈明，好久不见。\"]})\n",
    "print(final_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b79dc534-3c24-45d9-8c9c-0705c51f7b3c",
   "metadata": {},
   "source": [
    "一次对话内容如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "71f45f6f-0164-481f-a983-b8be310d8f55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='你好，我叫陈明，好久不见。', additional_kwargs={}, response_metadata={}, id='efecc8a9-dbe0-40ad-aedd-f3b741061ae2'),\n",
       " AIMessage(content='你好，陈明！确实好久不见，很高兴能和你再次联系。最近过得怎么样？一切还顺利吗？如果有任何想聊的或者需要帮助的地方，随时告诉我哦～ 😊', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 40, 'prompt_tokens': 12, 'total_tokens': 52, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '39c79867-f3ab-4476-843d-3c0b1f6f8ecd', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--5225dc1c-ab62-415c-a1b0-47d339fa49a0-0', usage_metadata={'input_tokens': 12, 'output_tokens': 40, 'total_tokens': 52, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "b9615585-0335-4ab1-bbdb-2f8c6f8ead2e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content='你好，我叫陈明，好久不见。', additional_kwargs={}, response_metadata={}, id='efecc8a9-dbe0-40ad-aedd-f3b741061ae2')"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "8c6af395-cdb4-4921-90c9-daf56e53ee96",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'efecc8a9-dbe0-40ad-aedd-f3b741061ae2'"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][0].id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "77b1f788-bc23-49fa-b74b-cb6c923203b5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'你好，我叫陈明，好久不见。'"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][0].content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "53aa35f9-16e4-49c0-9606-200667599402",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你好，陈明！确实好久不见，很高兴能和你再次联系。最近过得怎么样？一切还顺利吗？如果有任何想聊的或者需要帮助的地方，随时告诉我哦～ 😊', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 40, 'prompt_tokens': 12, 'total_tokens': 52, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '39c79867-f3ab-4476-843d-3c0b1f6f8ecd', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--5225dc1c-ab62-415c-a1b0-47d339fa49a0-0', usage_metadata={'input_tokens': 12, 'output_tokens': 40, 'total_tokens': 52, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "ba5f69b4-adcd-480a-8558-299a1ce14b03",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'run--5225dc1c-ab62-415c-a1b0-47d339fa49a0-0'"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][1].id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "8cbe3fe6-0b55-464d-bed9-afbb6316b088",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'你好，陈明！确实好久不见，很高兴能和你再次联系。最近过得怎么样？一切还顺利吗？如果有任何想聊的或者需要帮助的地方，随时告诉我哦～ 😊'"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][1].content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "bc0280ba-2d87-4ab5-acd1-6796074414c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "3b5dde40-f8d8-434e-8d6e-8e236f49112c",
   "metadata": {},
   "outputs": [],
   "source": [
    "messages_list = [\n",
    "    HumanMessage(content=\"你好，我叫陈明，好久不见。\"),\n",
    "    AIMessage(content=\"你好呀！我是小智，一名乐于助人的AI助手。很高兴认识你！\"),\n",
    "    HumanMessage(content=\"请问，你还记得我叫什么名字么？\"),\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "fa53721c-c1fb-40ab-9d8c-df4c8f270a47",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [HumanMessage(content='你好，我叫陈明，好久不见。', additional_kwargs={}, response_metadata={}, id='a87e253d-d451-46ee-aa08-27cfc2c02da5'), AIMessage(content='你好呀！我是小智，一名乐于助人的AI助手。很高兴认识你！', additional_kwargs={}, response_metadata={}, id='c3008865-f944-42bc-8aa6-32f3504e3ecc'), HumanMessage(content='请问，你还记得我叫什么名字么？', additional_kwargs={}, response_metadata={}, id='ec112d31-11d1-4684-b717-0c0ef9095164'), AIMessage(content='当然记得！你刚才说过你叫**陈明**，很高兴再次和你聊天～ 😊  \\n有什么我可以帮你的吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 27, 'prompt_tokens': 42, 'total_tokens': 69, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 42}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': 'e37759c1-dbaf-4e08-a0ff-4c517d96cb32', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--05c9c795-d3d2-4aa4-b527-c2125d1bd1ff-0', usage_metadata={'input_tokens': 42, 'output_tokens': 27, 'total_tokens': 69, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}\n"
     ]
    }
   ],
   "source": [
    "final_state = graph.invoke({\"messages\": messages_list})\n",
    "print(final_state)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "f42a5b5f-7d04-4bda-9f49-3b3c8797ace7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然记得！你刚才说过你叫**陈明**，很高兴再次和你聊天～ 😊  \\n有什么我可以帮你的吗？'"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "final_state['messages'][-1].content"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee2d3555-ed7d-4670-8835-a486ca56d092",
   "metadata": {},
   "source": [
    "&emsp;&emsp;据此，我们可以直接调用这个编译好的图`graph`，即可实现一个可交互式的聊天机器人。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "e968a713-310b-4fb7-9fc3-5664a5d185ee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户提问:  你好，我叫陈明，好久不见。\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🤖 小智： 你好，陈明！确实好久不见，很高兴能和你聊天。最近过得怎么样？有什么新鲜事想分享吗？或者有什么我可以帮你的呢？😊\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户提问:  你好记得我叫什么名字么？\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🤖 小智： 当然记得呀！你刚刚说过你叫**陈明**，对吧？😊 我记性还不错，不过如果中间隔太久或聊天记录清空的话可能会忘记～ 有什么需要帮忙的吗？\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户提问:  exit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "下次再见！\n"
     ]
    }
   ],
   "source": [
    "messages_list = []\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        user_input = input(\"用户提问: \")\n",
    "        if user_input.lower() in [\"exit\"]: \n",
    "            print(\"下次再见！\")\n",
    "            break\n",
    "        messages_list.append(HumanMessage(content=user_input))\n",
    "        final_state = graph.invoke({\"messages\": messages_list})\n",
    "        print(\"🤖 小智：\", final_state['messages'][-1].content)\n",
    "        messages_list.append(final_state['messages'][-1])\n",
    "        messages_list = messages_list[-50:]\n",
    "    except:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cec4e319-d536-47df-b115-f3f4fdba2816",
   "metadata": {},
   "source": [
    "- 借助MemorySaver高效搭建多轮对话机器人"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a837327-7c99-4870-86c9-fa98a2f9a427",
   "metadata": {},
   "source": [
    "🧠 MemorySaver 的核心功能\n",
    "\n",
    "1. **短期记忆（线程级记忆）**\n",
    "   MemorySaver 为每个 `thread_id` 保存和恢复对话状态（State），实现在同一会话中的历史上下文记忆。\n",
    "\n",
    "2. **状态持久化**\n",
    "   在每个节点运行后，State 会自动存储；再次调用时，如果使用相同的 `thread_id`，MemorySaver 会恢复此前保存的状态，无需手动传递历史信息 。\n",
    "\n",
    "3. **多会话隔离**\n",
    "   通过不同的 `thread_id` 可实现会话隔离，允许多个用户并发交互且各自的对话互不干扰。\n",
    "\n",
    "4. **图状态快照与恢复**\n",
    "   不仅包括对话历史，还保存整个工作流状态，可用于错误恢复、时间旅行、断点续跑、Human‑in‑the‑loop 等高级场景 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "567eb492-39d9-45c2-9523-6d198c1747ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "# 1. 定义状态类（会自动合并 messages）\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "# 2. 初始化模型\n",
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")\n",
    "\n",
    "# 3. 定义聊天节点\n",
    "def chatbot(state: State) -> State:\n",
    "    reply = model.invoke(state[\"messages\"])\n",
    "    return {\"messages\": [reply]}\n",
    "\n",
    "# 4. 构建带 MemorySaver 的图\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"chatbot\", chatbot)\n",
    "builder.add_edge(START, \"chatbot\")\n",
    "memory = MemorySaver()\n",
    "graph = builder.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "4e685cbb-f102-42ed-b813-04da86b8d6ce",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21af3ee8-2dd7-4c59-845e-025f37371bca",
   "metadata": {},
   "source": [
    "此时测试效果如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "908fbf87-6713-408b-94e1-5ab45237c618",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5. 运行多轮对话，使用相同 thread_id 实现记忆\n",
    "thread_config = {\"configurable\": {\"thread_id\": \"session_10\"}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "4c414a72-da8d-49fa-8d32-7f2989d05f8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第一轮对话\n",
    "state1 = graph.invoke({\"messages\": [{\"role\":\"user\",\"content\":\"你好，好久不见，我叫陈明。\"}]}, config=thread_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "f13bde7c-7390-4406-b884-326b6b3664d3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='你好，好久不见，我叫陈明。', additional_kwargs={}, response_metadata={}, id='a438c211-7673-4384-9828-d6171e988cb0'),\n",
       "  AIMessage(content='你好呀，陈明！确实好久不见～很高兴再次和你聊天。最近过得怎么样？有没有什么新鲜事或者想分享的故事？😊  \\n\\n（如果你愿意的话，可以随时聊聊近况、兴趣，或者任何你想讨论的话题～）', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 12, 'total_tokens': 63, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '85f976bc-e6a9-450b-9f88-59091d08db61', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7d936be1-afcf-4fda-bb36-16f02406270c-0', usage_metadata={'input_tokens': 12, 'output_tokens': 51, 'total_tokens': 63, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "cafecd18-7be3-4068-80c2-2e548fcbd54f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='你好，好久不见，我叫陈明。', additional_kwargs={}, response_metadata={}, id='a438c211-7673-4384-9828-d6171e988cb0'),\n",
       " AIMessage(content='你好呀，陈明！确实好久不见～很高兴再次和你聊天。最近过得怎么样？有没有什么新鲜事或者想分享的故事？😊  \\n\\n（如果你愿意的话，可以随时聊聊近况、兴趣，或者任何你想讨论的话题～）', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 12, 'total_tokens': 63, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '85f976bc-e6a9-450b-9f88-59091d08db61', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7d936be1-afcf-4fda-bb36-16f02406270c-0', usage_metadata={'input_tokens': 12, 'output_tokens': 51, 'total_tokens': 63, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state1['messages']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "7b06910b-c239-4b4f-9bf8-fe5317af3ec5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'你好呀，陈明！确实好久不见～很高兴再次和你聊天。最近过得怎么样？有没有什么新鲜事或者想分享的故事？😊  \\n\\n（如果你愿意的话，可以随时聊聊近况、兴趣，或者任何你想讨论的话题～）'"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state1['messages'][-1].content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "1339701b-15cd-4720-952f-faf210bd5294",
   "metadata": {},
   "outputs": [],
   "source": [
    "state2 = graph.invoke({\"messages\":[{\"role\":\"user\",\"content\":\"你好呀，你还记得我的名字吗？\"}]}, config=thread_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "6df1f1dd-6e11-4d96-8a1a-171f3d231b41",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='你好，好久不见，我叫陈明。', additional_kwargs={}, response_metadata={}, id='a438c211-7673-4384-9828-d6171e988cb0'),\n",
       " AIMessage(content='你好呀，陈明！确实好久不见～很高兴再次和你聊天。最近过得怎么样？有没有什么新鲜事或者想分享的故事？😊  \\n\\n（如果你愿意的话，可以随时聊聊近况、兴趣，或者任何你想讨论的话题～）', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 12, 'total_tokens': 63, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '85f976bc-e6a9-450b-9f88-59091d08db61', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7d936be1-afcf-4fda-bb36-16f02406270c-0', usage_metadata={'input_tokens': 12, 'output_tokens': 51, 'total_tokens': 63, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " HumanMessage(content='你好呀，你还记得我的名字吗？', additional_kwargs={}, response_metadata={}, id='4d9f1ee6-2840-414b-9ccf-a30366d21c38'),\n",
       " AIMessage(content='当然记得呀！你刚刚说过你叫**陈明**嘛～（笑）  \\n我的记性可是很好的，至少在当前对话中不会忘哦～  \\n不过如果太久没聊天（比如新对话时），可能需要你再提醒我一下下～ 😄  \\n\\n最近怎么样？陈明，这个名字听起来就很有故事感呢！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 69, 'prompt_tokens': 75, 'total_tokens': 144, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 75}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '56174361-efd1-42bc-ae79-7b46f539c0f8', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--271d878c-2d2f-44b6-9922-ad798649392b-0', usage_metadata={'input_tokens': 75, 'output_tokens': 69, 'total_tokens': 144, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state2['messages']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "030de3c4-88ab-4570-8542-eb33c4a8394c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然记得呀！你刚刚说过你叫**陈明**嘛～（笑）  \\n我的记性可是很好的，至少在当前对话中不会忘哦～  \\n不过如果太久没聊天（比如新对话时），可能需要你再提醒我一下下～ 😄  \\n\\n最近怎么样？陈明，这个名字听起来就很有故事感呢！'"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state2['messages'][-1].content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "ece85912-9c02-4503-983c-f3fc6a9e471a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然记得！不过作为AI助手，我实际上无法保留之前的对话记录或个人信息哦～每次新的对话开始时，我都会像初次见面一样。但你可以随时告诉我你的名字或任何信息，我会在本次对话中尽力为你提供帮助！ 😊 现在想让我怎么称呼你呢？'"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用不同 thread_id，会开启全新对话\n",
    "state3 = graph.invoke({\"messages\":[{\"role\":\"user\",\"content\":\"记得我的名字吗？\"}]}, \n",
    "                      config={\"configurable\":{\"thread_id\":\"session_2\"}})\n",
    "state3['messages'][-1].content"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a24dce1-5964-4f13-a7fd-f24216e6f9e8",
   "metadata": {},
   "source": [
    "- 查看记忆"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "id": "7a0d6cee-3862-48b8-b33b-e04da18b57ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "latest = graph.get_state(thread_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "d54feee0-4af3-4ac9-a176-0f77de5e9412",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[HumanMessage(content='你好，好久不见，我叫陈明。', additional_kwargs={}, response_metadata={}, id='a438c211-7673-4384-9828-d6171e988cb0'), AIMessage(content='你好呀，陈明！确实好久不见～很高兴再次和你聊天。最近过得怎么样？有没有什么新鲜事或者想分享的故事？😊  \\n\\n（如果你愿意的话，可以随时聊聊近况、兴趣，或者任何你想讨论的话题～）', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 12, 'total_tokens': 63, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '85f976bc-e6a9-450b-9f88-59091d08db61', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7d936be1-afcf-4fda-bb36-16f02406270c-0', usage_metadata={'input_tokens': 12, 'output_tokens': 51, 'total_tokens': 63, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}), HumanMessage(content='你好呀，你还记得我的名字吗？', additional_kwargs={}, response_metadata={}, id='4d9f1ee6-2840-414b-9ccf-a30366d21c38'), AIMessage(content='当然记得呀！你刚刚说过你叫**陈明**嘛～（笑）  \\n我的记性可是很好的，至少在当前对话中不会忘哦～  \\n不过如果太久没聊天（比如新对话时），可能需要你再提醒我一下下～ 😄  \\n\\n最近怎么样？陈明，这个名字听起来就很有故事感呢！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 69, 'prompt_tokens': 75, 'total_tokens': 144, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 75}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '56174361-efd1-42bc-ae79-7b46f539c0f8', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--271d878c-2d2f-44b6-9922-ad798649392b-0', usage_metadata={'input_tokens': 75, 'output_tokens': 69, 'total_tokens': 144, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]\n"
     ]
    }
   ],
   "source": [
    "print(latest.values[\"messages\"])  # 包含全部轮次对话"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1421ded9-e159-4fdf-980c-272d7510471c",
   "metadata": {},
   "source": [
    "### 2. LangGraph流式打印实现方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b90eb3c6-1d70-41f3-b050-24050c1341b2",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在实际应用中，流式输出尤其适用于需要快速反馈的业务场景，典型的应用场景就是聊天机器人，因为**大语言模型可能需要几秒钟才能生成对查询的完整响应，这远远慢于应用程序对最终用户的响应速度约为 200-300 毫秒的阈值**，如果是涉及多个大模型调用的复杂应用程序，这种延时会变得更加明显。让应用程序感觉响应更快的关键策略是显示中间进度；即，通过 `token` 流式传输大模型`Token`的输出，以此来显著提升用户体验。而在开发阶段，利用流式输出功能可以准确追踪到事件的具体执行阶段，并捕获相关数据，从而接入不同逻辑的数据处理和决策流程。是我们在应用开发中必须理解和掌握的技术点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8df9d3ea-4c2c-4cba-95c0-23a8629df8af",
   "metadata": {},
   "source": [
    "&emsp;&emsp;**流式输出，这种方式允许客户端逐渐接收到大模型生成的每一部分内容，而不是等待整个响应完成后一次性接收**。\n",
    "\n",
    "&emsp;&emsp;这里我们可以先看下使用`deepseek-chat`模型的流式调用代码及输出情况。在流式输出的实现方式中，我们需要在调用 `client.chat.completions.create()` 时添加 `stream=True` 参数，用以指定启用流式输出，从而允许 `API` 逐步发送生成的文本片段，然后使用 `for` 循环来迭代 `completion` 对象，每次循环接收到的 `chunk.choices[0].delta` 中会包含最新生成的文本片段。完整代码如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "890489c9-2579-4581-b687-aa541d425f35",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv \n",
    "load_dotenv(override=True)\n",
    "\n",
    "DeepSeek_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\n",
    "\n",
    "# print(DeepSeek_API_KEY)  # 可以通过打印查看"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "54d3133e-2e76-4e9b-9b4e-6af8e6e560e0",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChoiceDelta(content='', function_call=None, refusal=None, role='assistant', tool_calls=None)\n",
      "ChoiceDelta(content='你好', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='呀', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='！', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='😊', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我是', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Deep', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Se', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='ek', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' Chat', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='由', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='深度', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='求', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='索', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='（', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Deep', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Se', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='ek', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='）', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='公司', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='研发', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='智能', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' AI', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='助手', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='。', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='版本', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='是', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Deep', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Se', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='ek', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-V', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='3', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='知识', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='截止', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='日期', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='是', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='202', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='4', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='年', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='7', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='月', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='可以', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='处理', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='128', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='K', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='上下文', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='并', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='支持', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='文件', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='上传', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='（', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='如', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' PDF', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Word', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='Excel', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='等', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='）', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='进行', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='内容', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='分析', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='。', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n\\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='###', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ✨', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='特点', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：\\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='免费', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='使用', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='目前', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='不', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='收费', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='随时', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='为你', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='解答', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='问题', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='！', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='强大的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='理解', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='与', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='生成', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='能力', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='可以', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='帮你', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='写作', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='翻译', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='编程', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='学习', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='办公', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='等', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='。', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='超', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='长', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='上下文', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='记忆', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='能', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='记住', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='长达', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='128', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='K', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='对话', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='内容', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='适合', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='处理', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='长', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='文档', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='或', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='复杂', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='问题', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='。', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='文件', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='阅读', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='支持', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='上传', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='txt', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='pdf', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='ppt', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='word', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='ex', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='cel', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='等', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='文件', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='并', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='提取', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='关键', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='信息', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='。', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='-', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='持续', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='进步', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='团队', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='在', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='持续', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='优化', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='让我', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='变得更', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='聪明', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='更', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='贴心', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='！', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n\\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='###', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' 🛠', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我能', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='帮你', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='做什么', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='？', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='✅', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='学习', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' &', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='研究', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='解题', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='思路', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='论文', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='润', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='色', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='知识', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='科普', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='✅', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='工作', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' &', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='办公', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='写', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='报告', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='做', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='PPT', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='数据分析', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='邮件', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='撰写', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='✅', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='编程', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' &', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='技术', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='代码', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='调试', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='算法', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='讲解', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='项目', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='建议', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='✅', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='生活', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' &', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='娱乐', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='：', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='旅行', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='攻略', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='电影', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='推荐', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='聊天', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='陪伴', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='  \\n\\n', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='你可以', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='随时', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='向我', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='提问', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='，', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我会', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='尽力', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='提供', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' **', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='专业', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='准确', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='、', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='友好', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='**', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='的帮助', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='！', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='🎉', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content=' ', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='今天', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='有什么', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='我可以', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='帮', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='你的', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='吗', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='？', function_call=None, refusal=None, role=None, tool_calls=None)\n",
      "ChoiceDelta(content='', function_call=None, refusal=None, role=None, tool_calls=None)\n"
     ]
    }
   ],
   "source": [
    "from openai import OpenAI\n",
    "client = OpenAI( base_url=\"https://api.deepseek.com\",api_key=DeepSeek_API_KEY)\n",
    "\n",
    "completion = client.chat.completions.create(\n",
    "  model=\"deepseek-chat\",\n",
    "  messages=[\n",
    "    {\"role\": \"system\", \"content\": \"你是一位乐于助人的人工智能小助理。\"},\n",
    "    {\"role\": \"user\", \"content\": \"你好，请你介绍一下你自己。\"}\n",
    "  ],\n",
    "  stream=True\n",
    ")\n",
    "\n",
    "for chunk in completion:\n",
    "  print(chunk.choices[0].delta)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a772b45e-491e-45ad-b37a-a2424dd758c5",
   "metadata": {},
   "source": [
    "&emsp;&emsp;从输出上看，每个 `ChoiceDelta` 对象代表了大模型生成的一小段文本。所以很明显，这种流式调用的方式可以让我们看到 `DeepSeek` 模型是如何一步一步构建出最终的回答的。例如，从 \"你好\" 开始，接着是 \"！\"，然后是 \"我是\"，一直到整个回答完整地构建出来，每个 `ChoiceDelta` 可能包含几个字符到一个或几个词。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef0d8af1-b489-474f-a4f6-1229bd5d50ce",
   "metadata": {},
   "source": [
    "&emsp;&emsp;接下来，我们还是重点关注`LangGraph`中的流式输出应用方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b372a9ca-fe47-4647-9567-efcfcdb4e762",
   "metadata": {},
   "source": [
    "&emsp;&emsp;因为`LangGraph`框架的底层和 `LangChain`一样都是基于`LCEL`语法而构建的，所有直接把`LangChain`中的回调系统拿过来便可以使用。**在`LangChain`中的流式输出是：以块的形式传输最终输出，即一旦监测到有可用的块，就直接生成它。** 如下流程所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "200e55f8-48ec-454c-99f8-fb8a8a2128d9",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = init_chat_model(model=\"deepseek-chat\", model_provider=\"deepseek\")  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "a8f02263-facf-41bf-b691-2b3ae42e7a28",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "|你好|！|我是| **|Deep|Se|ek| Chat|**|，|由|深度|求|索|（|Deep|Se|ek|）|公司|研发|的一款|智能|对话|助手|。|我可以|帮助你|解答|问题|、|提供|建议|、|分析|数据|、|写作|辅助|、|编程|支持|等|，|还能|处理|上传|的文件|（|如|PDF|、|Word|、|Excel|等|）。|以下|是一些|关于|我的|详细信息|：\n",
      "\n",
      "|###| 🌟| **|我的|特点|**\n",
      "|1|.| **|知识|广|博|**|  \n",
      "|  | -| |我的|知识|截止|到|**|202|4|年|7|月|**|，|覆盖|科技|、|历史|、|文学|、|金融|、|医学|等多个|领域|。|  \n",
      "|  | -| |我能|提供|**|专业|、|准确|**|的信息|，|并|尽量|引用|可靠|来源|（|但|建议|你|交叉|验证|关键|信息|）。|  \n",
      "\n",
      "|2|.| **|超|长|上下文|理解|（|128|K|）|**|  \n",
      "|  | -| |可以|记住|并|分析|超|长|对话|或|文档|，|适合|处理|复杂|问题|、|论文|阅读|、|代码|分析|等|。|  \n",
      "\n",
      "|3|.| **|文件|阅读|与分析|**|  \n",
      "|  | -| |支持|上传| **|PDF|、|Word|、|Excel|、|PPT|、|T|XT|**| |等|文件|，|并|从中|提取|关键|信息|、|总结|内容|或|回答|相关问题|。|  \n",
      "\n",
      "|4|.| **|免费|使用|**|  \n",
      "|  | -| |目前|**|无需|付费|**|，|你可以|随时|向我|提问|，|我会|尽力|提供|帮助|！|  \n",
      "\n",
      "|5|.| **|多|语言|支持|**|  \n",
      "|  | -| |我|可以用|**|中文|、|英文|、|法语|、|德语|、|日语|**|等多种|语言|交流|，|并|帮助|翻译|或|润|色|文本|。|  \n",
      "\n",
      "|6|.| **|编程|与|技术支持|**|  \n",
      "|  | -| |熟悉| **|Python|、|C|++|、|Java|、|SQL|、|HTML|/C|SS|**| |等多种|编程|语言|，|能|帮助|调试|代码|、|优化|算法|或|讲解|技术|概念|。|  \n",
      "\n",
      "|###| 🚀| **|我能|帮你|做什么|？|**\n",
      "|✅| **|学习|与研究|**|：|解答|学术|问题|、|论文|阅读|、|知识|梳理|。|  \n",
      "|✅| **|写作|与|创意|**|：|文章|撰写|、|文案|优化|、|故事|创作|、|诗歌|生成|。|  \n",
      "|✅| **|办公|与|效率|**|：|文档|总结|、|数据分析|、|PPT|大纲|、|邮件|撰写|。|  \n",
      "|✅| **|编程|与技术|**|：|代码|调试|、|算法|讲解|、|技术|文档|解读|。|  \n",
      "|✅| **|生活|与|娱乐|**|：|旅行|建议|、|电影|推荐|、|心理|疏导|、|趣味|冷|知识|。|  \n",
      "\n",
      "|###| 📂| **|文件|处理|示例|**\n",
      "|如果你|上传|一份| **|PDF| |论文|**|，|我可以|：\n",
      "|-| |总结|核心|观点|  \n",
      "|-| |解释|专业|术语|  \n",
      "|-| |回答|关于|论文|的|细节|问题|  \n",
      "\n",
      "|如果你|上传| **|Excel| |表格|**|，|我可以|：\n",
      "|-| |分析|数据|趋势|  \n",
      "|-| |生成|统计|摘要|  \n",
      "|-| |提供|可视化|建议|  \n",
      "\n",
      "|###| ❤|️| **|我的|局限|**\n",
      "|-| **|不|联网|**|（|知识|截止|202|4|年|7|月|，|无法|提供|实时|信息|）。|  \n",
      "|-| **|不|涉及|医疗|诊断|、|法律|建议|**|（|仅|提供|一般|性|参考|）。|  \n",
      "|-| **|不会|生成|图片|/|音频|**|（|但|可以|描述|或|推荐|工具|）。|  \n",
      "\n",
      "|如果你|有任何|问题|，|尽管|问我|！|😊| |你今天|想|了解|什么|？||"
     ]
    }
   ],
   "source": [
    "chunks = []\n",
    "async for chunk in model.astream(\"你好，请你详细的介绍一下你自己。\"):\n",
    "    chunks.append(chunk)\n",
    "    print(chunk.content, end=\"|\", flush=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "b957d2b9-a908-4749-b792-85e6b495b74c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessageChunk(content='', additional_kwargs={}, response_metadata={}, id='run--c679d705-22fe-4713-9f2b-11bdd70fc43b')"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chunks[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6577925a-d3e6-4cc1-9aab-2ecd6621869f",
   "metadata": {},
   "source": [
    "&emsp;&emsp;每一个块，都是一个`AIMessageChunk`对象，用来代表`AIMessage`对象的一部分。消息块在设计上是可加的，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "d3a22688-2063-4151-892b-b1b579e07f0e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessageChunk(content='你好！我是 **', additional_kwargs={}, response_metadata={}, id='run--c679d705-22fe-4713-9f2b-11bdd70fc43b')"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chunks[0] + chunks[1] + chunks[2] + chunks[3] + chunks[4]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78c4e920-29f2-4fb8-a869-4ccc05512dcf",
   "metadata": {},
   "source": [
    "&emsp;&emsp;`LangGraph`框架中的工作流中由各个步骤的节点和边组成。这里的流式传输涉及在各个节点请求更新时跟踪图状态的变化。这样可以更精细地监控工作流中当前处于活动状态的节点，并在工作流经过不同阶段时提供有关工作流状态的实时更新。其实现方式也是和`LangChain`一样通过`.stream`和`.astream`方法执行流式输出，只不过适配到了图结构中。调用`.stream`和`.astream`方法时可以指定几种不同的模式，即：\n",
    "\n",
    "- **values** ：在图中的每个步骤之后流式传输**状态**的完整值。\n",
    "- **updates** ：在图中的每个步骤之后将更新流式传输到状态。如果在同一步骤中进行多个更新（例如运行多个节点），则这些更新将单独流式传输。\n",
    "- **debug** ：在整个图的执行过程中流式传输尽可能多的信息，主要用于调试程序。\n",
    "- **messages**：记录每个`messages`中的增量`token`。\n",
    "- **custom**：自定义流，通过`LangGraph 的 StreamWriter`方法实现。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82383a6d-95c9-4118-9fe6-c545847746a7",
   "metadata": {},
   "source": [
    "<div align=center><img src=\"https://muyu001.oss-cn-beijing.aliyuncs.com/img/image-20241028182221186.png\" width=80%></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4fdcf0d-b5f5-428f-adf0-66085159b2fb",
   "metadata": {},
   "source": [
    "&emsp;&emsp;如果我们想流式传输每个过程中的 `Tokens`，对当前图："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "d9c5e7e3-8bd1-4eb2-8bc7-c2ebb42e8267",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa4abd57-bdac-499d-b519-fa8104a19586",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这里调用`graph.stream`和`graph.astream`方法时, 指定模式为`messages`，即流式传输每个过程中的 `Tokens`。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "c0a4e61c-0838-43e8-ac3b-82e5f04fd228",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好！我是 **DeepSeek Chat**，由 **深度求索（DeepSeek）** 研发的一款 **智能AI助手**。我可以帮助你解答问题、提供信息、协助创作、分析数据等，致力于成为你的高效智能伙伴。以下是我的详细介绍：\n",
      "\n",
      "---\n",
      "\n",
      "### 🌟 **我的特点**\n",
      "1. **知识丰富**  \n",
      "   - 我的知识截止到 **2024年7月**，覆盖科技、历史、文学、经济、医学等多个领域。  \n",
      "   - 支持 **128K 上下文**，能理解和处理超长文本，比如论文、书籍、复杂代码等。  \n",
      "\n",
      "2. **文件阅读与分析**  \n",
      "   - 支持上传 **PDF、Word、Excel、PPT、TXT** 等文件，并从中提取关键信息进行分析。  \n",
      "   - 可以帮助你总结文档、翻译、提取数据，甚至解析代码。  \n",
      "\n",
      "3. **多语言支持**  \n",
      "   - 精通 **中英文**，也能处理法语、德语、日语等多种语言的文本。  \n",
      "   - 适用于翻译、润色、外语学习等场景。  \n",
      "\n",
      "4. **编程助手**  \n",
      "   - 熟悉 **Python、Java、C++、SQL、JavaScript** 等多种编程语言，能帮你调试代码、优化算法、解释技术概念。  \n",
      "   - 可以解析 **LeetCode** 题目，提供解题思路和代码示例。  \n",
      "\n",
      "5. **创意与写作**  \n",
      "   - 擅长 **写作辅助**，包括小说、论文、商业文案、邮件撰写等。  \n",
      "   - 能模仿不同风格，如正式报告、轻松幽默的对话等。  \n",
      "\n",
      "6. **免费使用**  \n",
      "   - 目前 **无需付费**，你可以随时向我提问，我会尽力提供高质量的回答！  \n",
      "\n",
      "---\n",
      "\n",
      "### 🛠 **我能帮你做什么？**\n",
      "✅ **学习与研究**：解答学术问题、论文润色、知识点解析。  \n",
      "✅ **办公效率**：总结报告、制作PPT大纲、处理Excel数据。  \n",
      "✅ **编程与技术**：代码调试、算法优化、技术文档解读。  \n",
      "✅ **生活助手**：旅行规划、健康建议、美食推荐。  \n",
      "✅ **娱乐创意**：写故事、生成诗歌、聊天解闷。  \n",
      "\n",
      "---\n",
      "\n",
      "### 📂 **文件处理示例**\n",
      "你可以上传一份 **PDF 论文**，让我：  \n",
      "📌 **总结核心观点**  \n",
      "📌 **提取关键数据**  \n",
      "📌 **翻译成英文/中文**  \n",
      "📌 **分析研究方法和结论**  \n",
      "\n",
      "或者上传 **Excel 表格**，让我：  \n",
      "📊 **计算统计数据**  \n",
      "📈 **生成可视化建议**  \n",
      "🔍 **查找异常值**  \n",
      "\n",
      "---\n",
      "\n",
      "### 🔍 **我和其他AI有什么不同？**\n",
      "- **超长上下文**：能记住并分析更长的对话和文档，减少信息丢失。  \n",
      "- **精准实用**：回答更注重逻辑和事实，避免无意义的“车轱辘话”。  \n",
      "- **持续进化**：团队在持续优化我的能力，未来会有更强的表现！  \n",
      "\n",
      "---\n",
      "\n",
      "💡 **你可以问我任何问题**，比如：  \n",
      "- “帮我写一封求职信。”  \n",
      "- “Python 如何用 Pandas 处理大数据？”  \n",
      "- “《百年孤独》讲的是什么？”  \n",
      "- “请分析我上传的这份财报。”  \n",
      "\n",
      "试试看，我会尽力帮你！😊"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "async for msg, metadata in graph.astream({\"messages\": [\"你好，请你详细的介绍一下你自己\"]}, stream_mode=\"messages\"):\n",
    "    if msg.content and not isinstance(msg, HumanMessage):\n",
    "        print(msg.content, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c102c12-029e-4773-b4cd-2e65ee1b0dc8",
   "metadata": {},
   "source": [
    "&emsp;&emsp;掌握`State`的定义模式和消息传递是`LangGraph`中最关键，也是构建应用最核心的部分，所有的高阶功能，如工具调用、上下文记忆，人机交互等依赖`State`的管理和使用，所以大家务必理解并掌握上述相关内容。这一部分我们在下一节课中展开详细的介绍，接下来我们先给大家介绍一个实用的开发工具：LangSmith。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
